
Join the Conversation!
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
"Please login to view comments"
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
Complete source code for this lesson is available at
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
##Looks like we found a thief monkey By the way, I liked the trick how you reached till here. You have a good sense of humor. You will improve a lot if you join our course with this passion.
var
(function-scoped, outdated)let
(block-scoped, modern and recommended)const
(block-scoped, cannot be reassigned)_
, or $
let let = 5;
is invalid)myVar
and myvar
are different)string
, number
, boolean
, null
, undefined
, bigint
, symbol
Objects
, Arrays
, Functions
Subscribing gives you access to a brief, insightful summary of each lecture to stay on track.
00:00:02 In this lesson, we'll make use of all of the actions we have created so far.
00:00:06 First, let's read or use the hasVoted action to show whether the user has voted.
00:00:13 Once again, there are so many ways of doing that.
00:00:16 First of all, we can call the hasVoted function directly within the question details page and pass value as props to the votes component.
00:00:25 That would look something like this.
00:00:27 ID question.
00:00:29 Remember, we were on that page for such a long time.
00:00:33 We can just call it right here and then pass it over to the votes component.
00:00:39 But if you've been thinking a bit more on a production level, then you may suggest doing a call on the client side.
00:00:45 We don't want the user to wait for everything to load.
00:00:48 Instead, they can see the question details as soon as possible, right?
00:00:52 So why not just delegate the hasVoted call on the client side?
00:00:56 That seems to be the ideal way, right?
00:00:58 But there is an even better and improved method using the latest React 19 hook called use.
00:01:06 Use is a React API that lets you read the value of a resource, like a promise or context.
00:01:13 You call it like this, const value is equal to use resource.
00:01:18 Unlike other hooks, this one can be called within loops and conditional if statements, and it integrates very well with suspense and error boundaries.
00:01:27 But one question that pops up often when thinking about using the use hook is, should I resolve a promise in a server or a client component?
00:01:37 So we can read through some of the details.
00:01:39 A promise can be passed from a server component to a client component and resolved in the client component with the use API.
00:01:48 You can also resolve the promise in a server component with a wait and pass the required data to the client component as a prop.
00:01:56 That looks something like this.
00:01:58 This is obviously a server component because we're using await, so we can very nicely await it right here and then pass it over into the client component.
00:02:07 But using await in a server component will block its rendering until the await statement is finished.
00:02:14 Instead, passing a promise from a server component to a client component prevents the promise from blocking the rendering of the server component.
00:02:23 Did you get that?
00:02:25 Instead of awaiting it right here and then passing the result to the client component, you can just pass the function itself,
00:02:32 and then the client component can make that call.
00:02:34 And as I mentioned already, the use hook works super well with using error boundaries and suspenses.
00:02:41 And unlike use effect, it prevents request waterfalls.
00:02:44 So, all in all, we get a better performance and user experience.
00:02:48 So in this lesson, I'll teach you how to use the use hook to resolve a promise, which we'll then send as a prop from the question details page to the votes component.
00:02:58 First things first, we have to modify our question details page to use the action and pass the unresolved promises to the votes component.
00:03:06 We can do that right here below the get answers call where we can say const HasVotedPromise.
00:03:13 Okay.
00:03:14 This is very important.
00:03:15 This will not be a value of HasVoted.
00:03:18 It'll contain the entire promise of HasVoted.
00:03:21 And I'll make that equal to HasVoted coming from lib actions, to which we have to pass a target ID of question dot underscore ID,
00:03:32 as well as a target type.
00:03:36 In this case, it'll be question.
00:03:38 Why is this a promise and not the value?
00:03:41 Because we haven't awaited it.
00:03:43 And we won't even do that in here.
00:03:44 Now we can head over to our votes and we can wrap it in suspense coming from React.
00:03:52 That'll look something like this.
00:03:54 And to this suspense, we can pass a fallback, which will simply be equal to a div that'll say loading.
00:04:02 So while it doesn't have the necessary data, it'll simply load.
00:04:06 And then right here at the bottom, we can pass hasVotedPromise equal to hasVotedPromise.
00:04:13 So let's dive into the votes component and let's accept this hasVotedPromise, which will be of a type promise that will result in an action response of hasVotedResponse.
00:04:30 And what is that?
00:04:31 Well, it'll simply either say hasUpVoted is true or hasDownVoted is true.
00:04:36 And here we can use the use hook.
00:04:39 Here's how it works.
00:04:41 we can say const, destructure the success as well as the data, and make it equal to the call of the use hook coming from React,
00:04:51 to which we can pass the entire hasVoted promise.
00:04:55 Of course, don't forget to destructure it right here from the forams.
00:04:59 Now, below this loading, we can destructure the hasUpVoted as well as hasDownVoted properties, from data or an empty object in case data doesn't exist.
00:05:11 I think we spelled them as hasUpvoted with the U letter being uppercased.
00:05:17 And in this case, the letter D being uppercased.
00:05:21 And now we can remove these fake hasUpvoted or hasDownvoted right here from the votes.
00:05:26 We no longer need that.
00:05:28 So just remove it.
00:05:29 Before they were hard-coded to true or false, that's in the question details component.
00:05:35 We just need to pass the upvotes.
00:05:37 the downvotes, we might as well pass the target type, which in this case will be equal to question, as well as the target ID,
00:05:48 which in this case will be equal to question dot underscore ID.
00:05:52 And finally, the has voted promise.
00:05:54 Now we can fix those up a bit in the params.
00:05:57 We'll have the target type, which will be either a question or an answer.
00:06:02 Next, the target ID, which will be of a type string, the number of upvotes, the number of downvotes, and then we don't need those two here because we're
00:06:12 extracting them by using the use hook directly from another server action.
00:06:16 And we can remove them from here.
00:06:17 Now you can see that I use different spellings in other cases, but I want to spell it like this.
00:06:22 HasUpvoted.
00:06:23 So you can select this one where I typed it wrongly and just fix it.
00:06:27 HasUpvoted.
00:06:28 And I think there's another occurrence here.
00:06:29 And now we can fix the HasDownvoted as well to have the proper naming.
00:06:37 Perfect.
00:06:38 Now, before we go ahead and test it, right here, we then handle vote.
00:06:42 The first thing at the top of the try block, we will add a const result, which will be equal to the call of the await create vote server action,
00:06:54 where we want to pass in a target ID.
00:06:58 As well as a target type and finally a vote type.
00:07:02 Of course, the target ID and target type are coming as props to this component.
00:07:07 So we can just get them right there, right here at the function props.
00:07:13 And now we can check if no result.success.
00:07:17 We're not going to throw an error, but rather we're going to return a toast saying failed to vote with a description of something like result.error?message.
00:07:29 Else, we will try to render a success message.
00:07:32 We already have the logic form to say whether the upvote has been added or removed or the downvote has been added or removed.
00:07:39 And then we have a catch.
00:07:41 Right here where we're showing this green icon, we can only show it if we have been successful in adding that upvote.
00:07:47 So we'll say if success is true and has upvoted, then show it.
00:07:53 And we're going to do the same thing for the downvote.
00:07:55 If success is true and has downvoted, now we can try to test it by clicking upvote right here.
00:08:02 And you'll see fail to vote.
00:08:04 Action type path is required.
00:08:07 And action ID is required and author path author is required.
00:08:12 So it's almost like we're not passing the right things into the create vote action.
00:08:17 Let's see if that's really the case.
00:08:18 Let's scroll down to where we're trying to create a vote.
00:08:21 Here where we're finding it, we're finding it based on the action ID and the action type, which we set to the target ID and the target type.
00:08:29 But what happens when we try to create it?
00:08:32 Oh, see, we just refer to those as target type and target ID.
00:08:36 But what we should do instead is, first of all, pass the author, which will be equal to the user ID.
00:08:44 Then we have to pass the action ID.
00:08:47 and make it equal to the target ID.
00:08:49 And finally, we have to pass the action type, which will be equal to the target type.
00:08:53 We also need to pass the vote type as well.
00:08:56 Sure, this could have been handled a bit more graciously by not coming up with two different names for the same thing, action ID and the target ID,
00:09:04 but sometimes this happens and we might need to refactor it later down the line.
00:09:09 But for now, let's see if this fixes the issue.
00:09:11 So if I reload and click UpVote right here, you can see that your vote has been recorded, which is amazing.
00:09:18 Exactly what we wanted.
00:09:20 But unfortunately, the UI didn't really update.
00:09:24 This arrow is not green.
00:09:26 What happens if I reload though?
00:09:28 Oh, there we go.
00:09:29 We have one vote and it is green.
00:09:32 But to fix this, to make sure that the results happen instantly after our user has voted, we have to use the Next.js's RevalidatePath function.
00:09:40 We'll use it right here within CreateVote, somewhere near the end, before the return.
00:09:47 I'll say RevalidatePath.
00:09:51 And specifically, we're going to refer to the routes dot question of a specific target ID.
00:10:01 And of course, we have to import revalidatePath from Next.js.
00:10:05 That is coming right here at the top, coming from Next cache.
00:10:09 What revalidatePath does is simply revalidates all the data on that path.
00:10:14 In this case, calling it after a vote has been created will regenerate or recall all calls to show the new data without a page reload.
00:10:23 For more info on exactly how revalidated path and more works in more detail, you can check out this advanced Next.js lesson.
00:10:31 It's actually a talk I gave for the Git Nation conference.
00:10:34 So you can check it out and hopefully it'll be useful in explaining a bit more deeply under the hood of how everything works.
00:10:41 I'll link it somewhere below this lesson.
00:10:43 But with that in mind, let's now go back over to our question and let me try to downvote it.
00:10:50 As you can see, it happens instantly.
00:10:52 Now, what if I try to downvote it once again?
00:10:54 As you can see, it removes the downvote.
00:10:57 If I do it once again, this is working perfect.
00:11:00 And what with the upvotes?
00:11:03 Okay, that's the same thing, but it looks like it was stuck to one upvote from before.
00:11:08 Interesting.
00:11:09 So if I downvote it and then click upvote, it looks like it doesn't remove the previous vote.
00:11:14 Oh, that's such an interesting bug.
00:11:16 I'm sure I missed something in the code.
00:11:18 So let's debug it in the next lesson.
00:11:20 Maybe you can try to find the issue before I do.
00:11:22 For now, I will commit this right here by saying Integrate Actions on UI, Commit it and sync it.
00:11:30 And let's go ahead and hunt that bug so we can finalize this question part of the votes, but then we can use the votes on the answers as well.