
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 create a new component that calls the add to collection server action and saves that question for the currently logged in user.
00:00:10 So to do that, let's head over into our components and let's create a new folder called questions.
00:00:18 And within questions, let's create a new file called saveQuestion.tsx.
00:00:25 Here, we can run RAFCE to quickly spin it up, and then we can call it within the question details page.
00:00:33 So yes, once again, we're heading over to the question ID details.
00:00:38 It's this one right here, where we call the votes.
00:00:42 And right below this suspense, we actually want to create another suspense with a fallback equal to div loading.
00:00:50 And directly within here, we can call the save question component that we just created.
00:00:58 And to it, we can pass the question ID equal to question dot underscore ID.
00:01:06 Perfect.
00:01:06 Now, if you go to the question details, you should be able to see it right here, save question right next to the votes.
00:01:13 So let's go into the save question and let's implement it.
00:01:17 This one will be a client rendered component because we'll have a click action on it.
00:01:22 So let me use the use client directive at the top.
00:01:25 And we'll only accept one prop, so that'll be a question ID.
00:01:30 And this is of a type.
00:01:32 Question ID is of a type string.
00:01:36 First things first, we have to get access to the session of the currently logged in user to know who is trying to save the question.
00:01:43 We can do that by saying const session is equal to use session.
00:01:48 And then from the session, we can extract the user ID by saying session question mark dot We'll also need some loading states,
00:02:02 so let me quickly create a new useState snippet called isLoading and setIsLoading at the start set to false.
00:02:10 And of course, don't forget to import useSession from next.react.
00:02:14 as well as use state from react after that we can start implementing the jsx of this component and trust me it'll be super simple the only thing it has
00:02:25 to do is return a single image and of course this image is coming from next image and it'll have a source equal to a string of forward slash icons forward
00:02:38 slash star dash filled dot svg it'll also have a width of about 18 a height of about 18 an alt tag of save and a class name equal to,
00:02:52 it'll be a template string of cursor-pointer so we know it's clickable.
00:02:57 And if it's loading, we will add the opacity of 50 so the user knows they cannot click it while it is loading.
00:03:06 We'll also add an area label equal to save question for screen readers.
00:03:12 And last but not least, and maybe the most important one, onClick, we will simply call the handleSave function.
00:03:22 So let's create it.
00:03:24 Right here, I'll say const handleSave and make it equal to an asynchronous function.
00:03:30 First, I'll check if there is no user ID.
00:03:33 I want to just return and maybe show some kind of a toast.
00:03:37 So I'll say return toast.
00:03:39 Don't forget to import the toast from hooks useToast.
00:03:43 And I'll pass in the title that's going to say something like, you need to be logged in to save a question and a variant equal to destructive.
00:03:54 Alongside checking whether a user exists, we can also check if we are currently loading.
00:03:59 So we can say if is loading.
00:04:01 In that case, we can just return because we cannot click it two times.
00:04:05 But if we're not loading, well, then we can start the loading.
00:04:09 So set is loading to true.
00:04:11 And we can open up a new try and catch block.
00:04:14 In the catch, I'll just show a toast where I'll render a title that'll say something like error.
00:04:22 I'll also render a description in this case, and we can render the error dot message coming from the catch.
00:04:29 But to actually make TypeScript not complain about the fact that the error is unknown, we have to say if error is an instance of error well then we know
00:04:42 that there will be an error message else we can just say an error occurred and I'll also set a variant to destructive and outside of the catch we can have
00:04:53 a finally block that'll happen no matter whether the try or catch fail or succeed where we can set the loading to false because whatever happens we want
00:05:03 to stop loading And you can see this little star up here on the top right.
00:05:07 Now, what will we do in the try?
00:05:10 Well, we can just call the server action.
00:05:13 Await, toggle, save question coming from the save question collection.
00:05:20 And we need to pass in an object that contains the question ID.
00:05:25 Out of that, we get back A couple of things, a success, a data, and an error if there is one.
00:05:34 So let's destructure them.
00:05:36 Once you do that, if there is no success, we can throw a new error of error message.
00:05:42 But if we do succeed, we can show a toast.
00:05:46 that'll have a title of something like a template string of question.
00:05:53 And then we can say either saved or removed.
00:05:55 So if data.saved is true, then we can say saved, else unsaved successfully.
00:06:01 And we can also show a variant.
00:06:04 If data is saved, then we can show something like success or maybe default, else it'll be destructive.
00:06:12 But it is possible that this data is undefined.
00:06:14 So I'll have to add a question mark right here saying that it's possible that the data is undefined.
00:06:20 And with that in mind, we are trying to save the question once we click on the star.
00:06:25 Let's also quickly create a single state called has saved, which by default can be set to false.
00:06:32 And that'll be used to change the icon from an empty state to a filled in state.
00:06:38 So we can say if has saved is true, then render the filled icon.
00:06:43 Else we can render forward slash icons, forward slash star dash red dot SVG.
00:06:52 So there we go.
00:06:52 So now we have an empty star and a full star.
00:06:55 This action is working well.
00:06:57 You can see question unsaved successfully.
00:07:00 And I gotta say, I think I made a mistake here.
00:07:02 Even though we're unsaving or deleting the question, the action still succeeded.
00:07:07 So there's no need to make it destructive either way.
00:07:11 It should always be successful, right?
00:07:13 Because we're successfully saving it or successfully removing it.
00:07:17 But with that said, there's no way for us to figure out what is the state of whether we have currently saved this question or not.
00:07:24 Sure, do we get back the answer after we click on it?
00:07:27 Yes.
00:07:28 But while we reload the page, there's not really a way for us to know whether the question has been saved or not.
00:07:35 So since in this lesson, we have implemented the save question server action within the code, we can commit it and sync it.
00:07:43 And in the next lesson, I'll teach you how to create another server action that'll allow us to see the current state of whether the user has saved or not
00:07:52 saved the question.