The next piece of functionality you’ll implement is the voting feature! Authenticated users are allowed to submit a vote for a link. The most upvoted links will later be displayed on a separate route!
Once more, the first step to implement this new feature is to make your React components ready for the expected functionality.
Open Link.js
and update render
to look as follows:
render() {
const authToken = localStorage.getItem(AUTH_TOKEN)
return (
<div className="flex mt2 items-start">
<div className="flex items-center">
<span className="gray">{this.props.index + 1}.</span>
{authToken && (
<div className="ml1 gray f11" onClick={() => this._voteForLink()}>
▲
</div>
)}
</div>
<div className="ml1">
<div>
{this.props.link.description} ({this.props.link.url})
</div>
<div className="f6 lh-copy gray">
{this.props.link.votes.length} votes | by{' '}
{this.props.link.postedBy
? this.props.link.postedBy.name
: 'Unknown'}{' '}
{timeDifferenceForDate(this.props.link.createdAt)}
</div>
</div>
</div>
)
}
You’re already preparing the Link
component to render the number of votes for each link and the name of the user that posted it. Plus you’ll render the upvote button if a user is currently logged in - that’s what you’re using the authToken
for. If the Link
is not associated with a User
, the user’s name will be displayed as Unknown
.
Notice that you’re also using a function called timeDifferenceForDate
that gets passed the createdAt
information for each link. The function will take the timestamp and convert it to a string that’s more user friendly, e.g. "3 hours ago"
.
Go ahead and implement the timeDifferenceForDate
function next so you can import and use it in the Link
component.
Finally, each Link
element will also render its position inside the list, so you have to pass down an index
from the LinkList
component.
Notice that the app won’t run at the moment since the votes
are not yet included in the query. You’ll fix that next!
All you do here is include information about the user who posted a link as well as information about the links’ votes in the query’s payload. You can now run the app again and the links will be properly displayed.
Note: If you’re not able to fetch the
Links
, restart the server and reload the browser. You could also check if everything is working as expected onGraphQL Playground
!
Let’s now move on and implement the vote
mutation!
This step should feel pretty familiar by now. You’re adding the ability to call the voteMutation
inside our functional component by using the <Mutation />
component (also we’re passing VOTE_MUTATION
and link.id
as props).
You can now go and test the implementation! Run yarn start
in hackernews-react-apollo
and click the upvote button on a link. You’re not getting any UI feedback yet, but after refreshing the page you’ll see the added votes.
Remember: You have to be
logged in
to being able to vote links!
In the next section, you’ll learn how to automatically update the UI after each mutation!
One cool thing about Apollo is that you can manually control the contents of the cache. This is really handy, especially after a mutation was performed. It allows to precisely determine how you want the cache to be updated. Here, you’ll use it to make sure the UI displays the correct number of votes right after the vote
mutation was performed.
You will implement this functionality by using Apollo’s caching data.
The update
function that you’re passing as prop to the <Mutation />
component will be called directly after the server returned the response. It receives the payload of the mutation (data
) and the current cache (store
) as arguments. You can then use this input to determine a new state for the cache.
Notice that you’re already destructuring the server response and retrieving the vote
field from it.
All right, so now you know what this update
function is, but the actual implementation will be done in the parent component of Link
, which is LinkList
.
What’s going on here?
FEED_QUERY
from the store
.votes
to the votes
that were just returned by the server.Next you need to pass this function down to the Link
so it can be called from there.
That’s it! The update
function will now be executed and make sure that the store gets updated properly after a mutation was performed. The store update will trigger a rerender of the component and thus update the UI with the correct information!
While we’re at it, let’s also implement update
for adding new links!
The update
function works in a very similar way as before. You first read the current state of the results of the FEED_QUERY
. Then you insert the newest link at beginning and write the query results back to the store.
Conversely, it also needs to be exported from where it is defined.
Awesome, now the store also updates with the right information after new links are added. The app is getting into shape 🤓