Next.js Server Actions (revalidatePath, useFormStatus & useOptimistic)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
everyone let's quickly talk about server actions and how they're going to replace your API routes so we'll walk through a simple example here so I'm on the to-do's page and these days in XTS you can have this server component and in those server components you can mark them as async and then in there you can use awaits and you can actually fetch data right so I'm using Prisma here as my orm for my database you don't need to know the Indian workings of Prisma so here you can see I have two to Do's in my database they each have an ID and they also have content so walk doc outside study for exam right so this is in my database and in this line here in my react component without using use effects I'm just fetching this data right and then what I'm doing here on the page I have an H1 I have a form right H1 and a form and then I want to Output it right here so here we have that list and I'm mapping over those two Do's right so I'm taking each to do and I have an Li I'm using the ID for the key and then I'm using the content for the actual text that you see here all right so that's what we can do in server components these days right so pretty cool this is possible these days but there's one thing even cooler which is that we can also mutate data meaning we can add a to-do with server actions so how would that work so in the past if I would type a to-do here and I would I want to add this to our list here in the past what you have to do in react here we have our form I collapsed it now I can open it so here we have that input and then we have this add button if you remember in the past you had to do some kind of on click for button or for the form we could have on on submit right so we could say on submit and then we could uh we could just call that handle submit right and then we could deal with that here right we would do something like this event.prevent default we would get the value from the from the input if that's a controlled input you know you had use state right and then you would you would use the state value you could of course also use event.target uh you would do some kind of fetch call right so you would do something like API to Do's method post body stringify blah blah blah headers right headers right so you would do something like this so you have some kind of API route that then on the back end you needed to make sure that you actually had that API route and then you had to hook that up with you know some controller some actual function that you want to run right so maybe if you were using next to S you could do that with uh you know for with the API folder and then you could create some route Handler right or maybe you were using you know xcos.post right you were using xeos same story you still had that API route that you had to create and and wire up with the actual code that you want to run right and now we're getting an error here by the way because I'm trying to add this handle submit to this form here but we're in a server component and it's telling us we cannot add these event handlers because server components are meant to be non-interactive right so we had to con we would have to convert this to a client component now we don't need to do all of this so we don't need to convert this to a client component we don't need to create this whole API route and wire it up with the with the with the function that we actually want to run when this form gets submitted we can use server actions so let me remove this and save here again and let me remove this as well and so these days what we can do if I now click adds here and we want to add that to our database here what we can do is we can use the action attribute you're going to hear the word action more and more in the world of react and xjs the idea here is that you want to do something an action is just a function that we want to run so here we want to add a to do so we could call that add to do and now we can create that function somewhere now you can in a server component you can actually just create it here so just like that handle submit function we can also just write that here if we want copilot is suggesting us async event so copilot is not completely up to date right so copilot has been trained on a lot of GitHub data so this is a very typical pattern but these days this is a bit new so sometimes you have to correct copilots here so here what we're going to get is form data and using typescript here it's going to be of typeform data we can run it like this right and then here at the top of the function here I'm going to say use server so this function will only run on the server so then if I want to get I want I need to grab the inputs the actual input here I gave it I gave this input a name of content and that's already in the form data right so here I don't have to use use date or anything like that right or like e dot targets and we already get the form data right here if you use this action attribute like this right with this this is already the server action right so the server action is right so this function is going to be the server action you can grab the input values like this you have grab the input values but now you know typically you would send that to some API route well not anymore we can do it right here we don't need to create this whole API route we can do it right here I can use this Prisma variable right here so I can say Prisma dot to do dot create right this is how you you can add something with Prisma my Prisma doesn't really matter it could be anything maybe you're using Mongoose for example I got started with Mongoose but now I'm using Prisma here right server action is just a function that will only run on the server right and also typically will invalidate we'll talk about that in a second so here I'm getting a warning from typescript because from data entry or null so basically what we need to do here for Prisma is we need to pass a string here this content needs to be a string typescript is telling us that this could also be null so here we know it's not going to be null because I output required here but timescript doesn't know that it says yeah it could technically be no this contents here we can write as a string so we can assert basically that this will always be a string right so let's do that right so this is what we can do now so if we save this now if we do this I'm going to get an error here because you need to mark it in your next config file you need to opt into this so here in next config I need to say experimental server actions true if you're using this make sure you do this in your next config file as of recording this is still necessary but you know if you're watching this in a year from now probably not I'm Gonna Save here now I've noticed that whenever I change my next config file that my Dev server crashes yeah so here it crashes again so I need to manually restart it here npm run Dev okay so it's running again Let me refresh here all right so now the error is gone so let's go back here the form has been has been changed now to run this action the add to do action when the form gets submitted so let's try that now so now let's say cook dinner cook dinner now I'm gonna click as and this action should get invoked so I can I'm going to click add here and before I click adds here let me also add a weight here right this is asynchronous so I'm going to await this so now I'm going to save Here and Now what we're going to do is we're going to click on ADD so let's see what happens now I'm going to click add here and we don't see anything happening this data here is still the old data so we haven't invalidated this as it's called right so here this action should now have been invoked so this code now should have been run so let's press my code here now I should have created some entry into our database now so if I re go back to the database here and refresh my database let's see if we see an extra item and indeed we do right so you can see now we have content cook dinner in our database now so the database is correct now but now of course we need to make sure that the UI is also correct and this UI is still working with these to Do's that were fetched when we first loaded the page we need to invalidate this as it's called we basically want to tell next to us this is invalid now get the latest data right so basically we want to run this again this this line of code again and we can do that we can say revalidate this path I can imported from next cache and I can tell nextgens this path that we are on forward slash to Do's which is where we were fetching this this is now invalid right so after we edit this the current data is invalid so it needs to be revalidated right so it needs to be refetched right so let's add this and then let's try this again and actually when I save the file it actually already adds this for me but let's try again another entry here let's uh say I need to do the dishes so now I'm going to click adds here and let's see what happens and that could take some time yeah and now after like two seconds or so it also adds this automatically right so it's going to take one or two seconds before uh this is finished right we're awaiting that here so it could take one or two seconds and then it gets to this revalidate path line which will refetch the data here right so first it's going to add it to the database once it's in the database we revalidate the data meaning it's going to find all of the entries in the database again right so to do's and it will it will just map over those to-do's again over all of them right and if I refresh I can see that the database will be up to to that right so now you can see there's another entry here do the dishes so database up to date UI is up to date and we haven't even created some you know forward slash API so how is this possible actually because it looks so strange because you know if you're used to react you know you're working on the front end and here we're sort of interacting with a database all of a sudden so this looks very strange but but let's take a look at the dev tools so you can see that there is actually still communication between client and server but it's handled by next.js right so we are not manually creating an API route behind the scenes next to us sort of is doing that so let's see here I can go to network and here it's uh the network tab right so we can see all the network requests so here if I type something if I just type test let's write type test here if I click as here you can see there is a fetch call actually and so there is something going from client to server here if I click there you can see there is something going here to the server and this is not really interesting but if we look at payload for example we can see that there is some form data going from client to server so we can see one underscore content basically the name of our input field is content and we can see the actual value test and then there is also some action ID and this is not done by us this is done by next.js right and some other stuff here so probably some identifiers that next to us uses under the hood to you know match up this server function or server action with what we're doing here in this form right so we don't have to manually create these API routes anymore next.js does that for us automatically right so that's one of the benefits you don't have to create these apis anymore and another benefit is that this will work even without JavaScript enabled so if the user turned off their JavaScript or the JavaScript hasn't loaded yet or the JavaScript for whatever reason fails to load on the page this will still work it's also called Progressive enhancement and it also means that the client-side bundle will be smaller because you don't need a JavaScript so the JavaScript doesn't need to be included in that client-side bundle another benefit is here this function runs on the server so we can update our database and then immediately revalidate so in the past on an API route you would update your database right and then go to the client again and then the clients had to make another Network call to to get the latest data here we're doing both of that in one network call right so it will go from the client to the server updated database and then immediately revalidate the data here right so this is a pretty powerful combination we can fetch data like this and then we can update our data like the server mutations as it's called and so together you could even make the argument that this is going to replace a react query or maybe SWR in case you were using that I'll have a separate video on that make sure you subscribe all right now in practice what you also want to do is for example reset the form right and you also want to show like a loading indicator because now if I click on ADD it just we don't really get feedback that something is being submitted right so typically in the real world you do want to have some client-side interactivity here we also want to validate the inputs from the user here on the client for example and on the server as well but also on the client right so if we look at our components here this is a server component this H1 can stay on the server right but then here with the Forum if I Collapse this typically we want to do some validation before we actually invoke the server action we want to maybe also reset the form so we want to use some client-side interactivity so let's refactor this form into its own component make and Mark that as a client component so I will go here into components let's call that just form quickly create form component I will just copy everything from here pasting it here and then instead of writing this here we now want to have R4 right let's import this right so now we have this and we want to have some interactivity here on the client like reset form Etc so we're going to make this a client component so I'm going to mark this as use client and now it's interesting because now of course we need to add this add to do server action but now it's a client component so here we were using this is a server component so in a server component you can have this function here right here in the component you can mark it as a user version like this you cannot do that in a client component right so if you want to use a server action like this in a client component put it somewhere else and then import it in here so what you're going to have in your project is you're going to have some actions folder let's see if I can add an actions folder here we have some actions and maybe you can even create a file per action but let's just create one actions.ts and I'm going to paste our action server action here server actually needs access to our Prisma variable we can just import that revalidate path as well okay so now let's remove this to recap here our to-do's page is still a server component it can still fetch data and map over that here but now we have a form component which is a client component and I have separate videos on clients and server components make sure you check out my other videos in this client component now we can import that action that with a separate file right and what we can do is instead of having this user for every action what we need to do here is we need to put U server at the top of the file so every function that you export from here export this function will be a server action right so now if we export it like this we have U server at the top now we can import that I can import it like this and this is a client component so you can invoke a server action from a client component no problem so now if I save here you can see the errors are gone so this should work again so let's quickly test it let's test five and click adds here and let's see if that works and yeah eventually it test 5 still works right so we have successfully refactored this to a client's component this form because what we want to do in the real world is we want to give some feedback while it's being submitted right we want to reset the form we maybe we want to do some validation here on the client so typically you're not going to invoke your server action like this what you're going to do is typically you're going to have a function in here actually and we saw that already we were already depending on that form data we already got that here right so we get formed when you when you write a function you can you can now in next.js applications you can write a function here for the action attribute this doesn't work outside next to us right so here you you immediately get form data like this right so in HTML you pass like a URL to action and the browser would submit it submit form data to that URL right so next year us and also remakes they're trying to replicate that so here what you can pass as a function you immediately get access to the form data and then in here you can still call that server action right so the server action let's just pass the form data to that server action form data we want to make this an asynchronous function so async and then we're going to always server action right so what we can do is before we invoke the server action we may just want to reset the form so to reset the form it's not it's not a controlled component here so what we can do instead is we can use user ref so I'll quickly create a ref for the form is use ref and this is going to be for typescript this will be a HTML form element so now we can use ref on the form I'll just add that here actually from ref srf okay so now what we can do is before we invoke the server action here on the client we can say ref.current dot reset reset this input and we're going to get a warning from typescript because ref.current could be no we actually initialize it as a null call reset that or no so we want to have a question mark here you know optional chaining here that we get rid of that error here and then after it's reset we're going to invoke the server action a bit complicated here I'm going to save it it's already structured a little bit better but bear with me right so all the code here will be on the client here so if we add input validation and other things first run in the browser and only when we call this function will this form actually get submitted to this function here and this will run on the server I'm going to save here and I will just write test number nine I add here let's see what happens immediately it's clears and after a couple seconds yeah so after a couple seconds it indeed gets added to list here so everything still works we now also have some client-side interactivity here or some client-side features so we can make this a little bit more like real world scenario right so you can do other things in here as well by the way the downside of using a server action in a client component is that you lose a lot of that Progressive enhancement so now so that's one of the downsides of using it like this now in the real world you don't really have a choice you do you do need that you know client-sized validation sometimes you want to reset things in I think the in the most in the vast majority of cases you're gonna have that server action being invoked in a client component all right now typically we also want to show like a pending state so when while it's being submitted that it can show something like adding to do dot dot so we actually have a very cool hook that we also get in conjunction with the concept of server actions we get some will be useful for this unfortunately to make that work you do need to make that a client component of the form so you need to do that the form needs to be an ancestor essentially so what we're going to do is we're going to refactor this button into its own component so I'm quickly going to create a button component I'm going to copy this remove it from here just paste it right here oh actually let me create this paste it right here so we have a button here we're now going to have a button and I will import that right so it works the exact same if I say if it looks the exact same works the exact same but now this button is its own component and the hook that we can use here form status and this is still experimental so here if you import this it's going to be this with this X experimental underscore and we don't want to refer to it like that so we can Alias this so we can say s use form status so here I'm going to remove this so we can call this and this will give us well let's actually see what we get so here we can destructure these four things so we can say we want to have action this will actually show us the the function the actual action that is the server action that's connected to that form and some data the form data the methods and what we what you emotional time want is the spending value so now it will show you this form is being submitted it will show you that it's pending and so here we can use that variable here to uh to display something in case that's true in case it's pending so here we have pending well if it's pending we don't want to say loading we want to say adding to do dot dots otherwise it should still be okay so now if I save here and let's try uh let's try doing this again so I'm going to say I'm going to code an app so now I'm going to click adds here let's see what happens and you can see the button immediately changes to adding to do it does show code app at the end of the list there's a bit of like there at the end I'm not sure why that is actually and some of this is still experimental so so it doesn't work perfectly yet but this is some this is what we want essentially so this will not work if you do it here right so it needs to be that the form needs to be like an ancestor this is most of what we want with forms right so we have like a loading State and you have some client sites uh validation resetting the form there's one other thing in the real world that you also want which is error handling so what if this database call in our server action what if that goes wrong this is running on the server right so how do we make sure that if it goes wrong that we can output some message to the client to the user this is its own separate topic so I'll create a separate video about that but just to give you a quick sneak peek we can just wrap this and try catch and so we can try we can attempt to update our database if this doesn't work and some error is thrown we can catch the error right we can just catch that and then here what you can do you can simply return that to the client so you can just return let's say an object with error that's going to be this e right so you can just return something in this function and that's what automatically gets sent back to the client so let's see where we invoke This Server action in the form here here where we invoke the server action now we may get back so we can just an object with error right so we can immediately destructure that right so here we can just immediately get back an error and if there is an error so if error we can just output you know alerts or maybe a toast message but here we'll say right I have error.essage here all right so this would be a quick example right so here you would just catch the error right and just return something to the client just like any other function destructure it like that and you can check if there is an error we can alert or do a toast message now it's a bigger topic so I'll create a separate video on that right so I'm just going to remove this for now but that's just an example of another like real world element that you do need right so you do need to be able to deal with errors and so I remove it here for now just this is just a simple example alright so also let me clear the database here just so we don't run off the page here so I'm just going to remove some of these to Do's alright so just removed some uh to Do's here so we now have three left I said there's another very cool hook with this and this will also be the future of of UI so we need to know how it works and it's called use optimistic so here these to Do's right now they are always fetched here so these to Do's are always coming from the database add a to-do here we know that most of the time this will be successful so we might as well just immediately add this here we don't want to wait those two or three seconds before it it has been added to the database and then it has been fetched again ideally we can just immediately add this here and if for whatever reason the you know something went wrong we just referred we remove it again but most of the time it's going to work all right so this is called optimistic UI so to make that work we're going to add this list also here to that to the form those need to be in the same component here so let's just add that here and we'll add the list here and we need to react elements here not two so we need to wrap this in a reacts fragment when I don't use div no reason to add diff you're just gonna pollute the HTML For No Good Reason alright so then it says to Do's is not defined so here to this form and the form is not really a good name for this component anymore so let me quickly rename this to I will just call that two Deuce component click yes and this will now be to Do's components to Do's component import this properly all right so a bit of refactoring here that's the real world as well all right so what we have now is still our server component still the page this page is still fetching this data and now we have this to Do's components which is a client component which holds everything else right including here this form with the action here so now here we also have this list where we're mapping over all the to Do's so we need to pass the to-do's here from the server to the client we can just do that with props pass it like this so now this to-do's component will take in so we can say to those and I'm using typescript here so let me quickly type this to those components so I'm using type here I have another video on this don't use interface use type to do is component props that's actually going to be an array of to do so let's type A to do it's just an object with an ID and actually ID is a number ID is a number and content is a string and then we can just say this is going to be an array of that so the props themselves there are there's an objects what we're going to get is to Do's which is an array of these students don't worry if you're not on top screen it's not really important here but what we have now is we can we get these to Do's now so we can map over them right they're fetched on the page we're mapping over that here that's enough if I add one it still works so test three I click a three here if I click adds here and we see that everything is still working right so it still works all right now you could see though that it takes some time before it's actually displayed here and in the real world a really nice UI pattern is when you when you are optimistic so you immediately want to show this you immediately want to add that to the list here and we can do that with another really cool hook that will also work really nice with in conjunction with these server actions right so this is all connected to server actions so what the hook is optimistic so use optimistic and this is also experimental at the time of recording it's gonna have this ugly name so that's Alias this as then we can just use it like that optimistic okay we need to pass two things in here so the initial to Do's uh it's going to be a function it's gonna look a little bit complicated so this function will take in it will give us the the states the current states and then also the new to do that we will pass and this function it's this is called the reducer pattern so maybe you've seen this before you see it in Redux for example as well so what we simply want to do is um we take the state we just spread it in a new array and then we just want to add the new to do here as well so what we get here is two things we get the optimistic so basically the optimistic version of other to do's and then also we can an optimistic to do okay so syntax here is a bit ugly let's see but saying that new to do is of type and we can just say this is going to be a type to do I have a separate video on the unknown type in typescript so now this is a bunch of code but basically what we get with this use optimistic is now instead of using the to Do's the server we want to have a more optimistic version of that so that's what we get here optimistic to do so now we don't want to map over the you know the traditional we want to map over the optimistic to those so now if I say fair we look at the page you can see nothing has changed but now what we want to do is before we actually add it to our database and before we have to wait those two three seconds we can already optimistically add it to the list here and that's what we can do with ADD optimistic to do so here if we call add optimistic to do on to do as an objects and has an ID and we'll just use a random random ID here and then the content is going to be the same as before right so from the input and then we will assert that it's going to be a string and so now before we we make that round trip to the server we're already going to assume that it's going to be successful so it will be added here at least that's the idea here so now if I save here and now we try adding this I will write cool test here and now I'm going to click adds here and you can see it immediately gets added here to the list if I check the database it should still be added there as well yeah so in a database it has also been correct so that's a really cool I think now we don't want to have that button anymore show the pending State like that you know get some kind of loading State when it already has so now we can just uncomment this and just always use now one reason to use pending here as well is to disable this button so so here we want to disable the button when it's bending right now we don't want to have this pending State like that anymore we still want to use it for disabled here but we don't we just want to show this is really nice UI pattern right so it feels very Snappy to the user if you have this in your apps so if I have another test test five if I click add again you can see it immediately gets added here super Snappy to the user really nice pattern all right now what happens because very optimistic now but what happens if if something goes wrong on the server and we don't actually save it to the server so then it should be removed again it should be reverted one of the benefits of using it in conjunction with server actions because if we go to our server action now it's in this action actions file so here if something goes wrong here if I just wrap this and try catch right and I'll have a separate video on dealing with errors I'll just leave that empty for now but let's say something goes wrong here I'm using form data.content right but let's say I I'm using right so I'm just make a typo here let's say I get an issue with typescript I'm just going to ignore that just an example of something going wrong in This Server action so now we are optimistic and we you think it's going to work alright but now in the action it's going to be wrong right so it's not going to be properly added to the database now but on the UI it's still going to be shown so how does so we need to make sure that when it fails that it's actually removed again from the UI so let's try that again it's going to be the final test that we do so I will say final test I'm going to click add here initially it's added now you can see and let's see and you can see it automatically is removed and so you can see now this is super powerful with these server actions not only you know don't we have to create these API routes but you also get these hooks these add optimistic hook use form status you know for the pending State all right so very quickly you can use these server actions with this action attribute on the form that's what we've done but you could also add that you can also do this with some other element in the form so you can also use form action so not action but form action on the button for example or even an input right so here they show an example you could have an image upload you can have a server action that will run when that occurs right you don't need to create a whole API route you know slash API upload image next.js will handle that for you under the hood you can use this specific server action only to this input right so this is mostly useful if you want to have different server actions for different inputs or you have two buttons you want to have one server action for one button and then another one for the other button now you can actually also invoke server actions outside forms and they recommend to use this use transition hook but you don't seem to need this so you can even call this without the use transition Hook from react this looks very complicated so you have this start transition this use transition Hook is actually used to deprioritize State update so it's a bit confusing that now it's being used in conjunction with these server actions and but from from looking around you don't need to use this hook there's a benefit probably that comes with this right but this will completely disable the out of the box Progressive enhancement but then you can use it with outside forums right and here they also mention custom unification without that star transition right so without the use transition hook it's also still possible but if you're really interested in that it's already pretty Advanced so then I would just recommend you go through the docs alright so I hope this was helpful now I have my reacting next.js course coming up soon highly recommend you get one email list so you'll be notified when I release it and if the video was helpful I really appreciate it if you could like And subscribe with notifications on check out my other videos and then I hope to see you soon
Info
Channel: ByteGrad
Views: 123,744
Rating: undefined out of 5
Keywords: next.js, nextjs, next.js server actions, react, vercel
Id: RadgkoJrhu0
Channel Id: undefined
Length: 28min 6sec (1686 seconds)
Published: Sat Jul 22 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.