Next.js 14 Server Actions | Everything you need to know!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so few days ago the NEX Shar team held its yearly NEX Shar conf and inside of this conference they have now finally released NEX Shar 14 and I will admit there's not a lot of new things but there's one interesting thing and that's that NEX share server actions are not finally stable and now the question arises how the hell do I use next share server actions and that's why I've created this video today so you can learn how to use them so in this video I want to talk about what are server actions how do you use server actions how do I revalidate data how do I show a pending State how do I do ER handling and also I want to show you how to clear form submissions after your form has been submitted so that's all what we'll do today so STP in and let's go so if you can remember before we had this whole NEX server action in the NEX 13 release we would manually create API ws and the nice thing with serve actions is that we don't have to do that anymore we can now create asynchronous functions inside of our component and with that we can then mutate our data so now what's the nice thing with server actions well server actions can be defined inside of a server component or they can be called inside of a client component and now the nice thing with server actions is if I use them inside of a server component the whole form submission Works without any JavaScript so zero KB that's very nice and that's why I really like to use them and also because they're very easy to right actually you don't have to do all of the spoiler plate for API RS you only create a async function and boom you have your server action and now we I've talked a bit about server actions I think we can now start with the fun stuff and create some actual server actions so let's go so to get started I will CD into my desktop directory CD into my YouTube directory and then inside of here I will R npx create-- app at latest since I want to install the latest package of nextjs the CLI will now ask me for name so let's name it nextjs um server actions YouTube then I will use typescript I will use eand I will use tnd I won't use the source directory since we will use the app router and then I won't customize the default import Alias now the installation will take a few seconds so once that's done I will come back so the CLI has now finally installed nextjs and I can now go to my visual studio code and as you see I already opened the project and if you want to do the same click right here on file and then click on open folder but right now we just have a default template and as you see it's pretty much empty and we don't have anything we can also see that if we uh start our def server so let's open the terminal you can either use command J or you can go on top and click on Terminal and new terminal but inside of here let's run npm R def this will now start our def server no close 3000 so let's open that so as you see we now have the default template that next Shar ships with and we don't want to leave it like that we want to create a basic to-do application and then with the to-do application we can test out server actions so to create a to-do to edit a to-do and to delete a to-do so I think let's do that and before we can actually start with the UI I want to install a few things so for that let's stop our def server and let me clear everything and then I want to install Prisma and why do I need Prisma that's because I want to query my database from super base that's right we will use superbase for our database and then inside of our database we will store our to-dos and at the end also manipulate them so for that let's install Prisma so let's do an npmi Das capitalize D Prisma and with that I install the development dependency of Prisma and the development dependency means that the uh this dependency or the size of this dependency will no will not show up in our production build also I need to install at Prisma / client and this will now be a normal dependency which will show up in our production bundle so once that's installed we can now initialize Prisma with npx Prisma um in it and this again will just initialize Prisma and you can see that we have now created two files uh one environment variable file with a default database URL and a Prisma folder with an empty schema let's first of all create a database at superbase so let's go to super base.com so I'm now at Super base.com and as already said superbase is what we'll use for our database provider but superbase is much more than a database provider actually they are a full-fledged open-source Firebase alternative and that means that they have a post offering authentication instant apis Edge functions real time subscriptions and storage but for us we only need our postest database but if you want to check it out go to superb.com and now we can click right here on dashboard and if you don't have an account create one and then let me click on new project from I will choose this organization let's choose the name let's just name it next J server actions then I will have to generate a password right here and I will also copy that since we will need it in a second I will just paste it right now in myv file then for the region I will choose frankurt and Frankfurt is the nearest to me but you should choose the one that's nearest to you now I can click on create new project this will now take a bit about 2 minutes so once that's done I will come back so great uh super base has now finally initialized so now I can click right here on Project settings then I can click on database and then if if I scroll down we now have our connection string so let me click on U since we all need that and let me copy that if I now go back to my environment variable I can delete this default database URL since we now have an actual database URL and paste that now you will see right here we have an option to paste our password and I've copied that right here as you see so let me copy that again and now let's change this your password to the actual password so that looks good and now we can actually continue and create an actual schema now for the schema this will be a absolutely a simple schema we'll just create one model we'll call it to-do and let's open that we'll have to give it an ID as always this will be in string let's give it an add which will be ID and at at default which will be uu ID and with The UU ID I just create a unique identifier and I don't have to do anything myself uh pisma will create one um by itself what else do we need I want to have an input and the input is just a different name for our to-do message and this will be of type string and I also want to have an created at um timestamp since I want to uh later on um pretty much sort my to-do by date so this will be of type date time and let's give it an add default of now and with now I just tell Prisma to create a new um Tim stamp when I create a new record so that now looks good so let's push our schema to our database and for that let's open the terminal and run npx Prisma DB um push and with that I just push my current schema to our uh database at super base you now see our database is in sync with our Prisma schema so that looks good but now we're still not done um we have now created our schema that looks good but we have to create a Prisma client with which we can then query our database and since we use um nexts which uses HMR so hot module reloading we have to create a global Prisma instance in the development environment so if you go to Google right now and just Google nextjs and best practices um Prisma just like that let me search that and if I open the first right here result you will see best practices for initializing pisma client with next shares and you see right here the problem uh and it tells us lots of users have come across this warning uh while working with next shares and the warning is that Prisma client and there are already 10 instances of Prisma client actively running so that means each time we have hot module reloading it will uh generate a new Prisma uh client instance which is actively running and you don't want to do that in the development environment you only want to have one Global uh Prisma client so right here you have an solution and that's just to check if you're not inside of the production environment then you can create a global Prisma file uh Global Prisma instance I'm sorry so let me just copy this right here and then let's go back let me open the Explorer and then inside of the app folder I will create a new file called db. TS and inside of here I can now paste the code and again we just check if we're in a production environment or if we are actually not in a production environment and if we are not we create a global Prisma file and if we are in a um production environment we create the normal Prisma client so now that looks good we have now finally created Prisma and it's working so what we can do now is actually start with the fun stuff which is the UI and the server actions so for that let me go inside of this page. TSX for now and let me actually delete everything I don't want this default template I don't need it and then let me just uh return a H1 and let's say hello from the index page to see if everything works for that let me again open my terminal let me clear everything and let's run npm1 def to Smart uh to start my def server on low close 3000 so if I go back to low close 3000 this will now open in a second so as you see now we have this hello from the index page that looks all right but right now we have a black background I don't want that so let's change that for that I will now open the global. CSS file let me delete everything uh after this at tent classes so everything this root and body and and let's create a new styling so body I want to Target the body and give it in background color of white so if I now go back we now see a white background and I think that's much easier to read so now I think we can actually create our form to create our to-dos so let's do that let me delete this return statement and let me just create a new return statement then let's create a diff element let's give it a class name and let's give it some default styling of a H screen um W screen Flex items Center and justify center with this I just tell all of the child elements of this thff element to just group in the middle of the screen so we give it in height of 100 viewport height width of 100 viewport height and then Flex of item Center and justify Center then inside of here let's create a child div let's give it a class name which will just be border rounded of LG and shadow of let's say XL I have to spell that correctly so let's do that and let's also give it an padding of 10 and I want to give this uh diff element a custom width so let's give it in width with these custom array brackets so with this array brackets I can give it a custom value and the custom value will be 30 VW so 30 viewport width if I now create inside of this div and H1 to see it I will just say hello save that and go back you will now see right here we have now this sff element with an Bora and the shadow but I don't want this H1 so let me delete that this was just for demo purposes inside of here let me create a normal form element and I won't need this action right now so let me just delete it I will use it later on and let me also give it in class name so the class name will be flex and let's give it in Flex of call also inside of here I want to now create a input the input will be of types text and let me also give it a name and the name will just be called input and that looks good if I now save that and go back you will now see actually nothing and that's weird right we have just created an input and that's uh because of tyon CSS magic or not magic um tyn CSS buffers or not buffers but default Styles our input as actually nothing so we don't see it I can of course click it and type it and now you see our input but the default style of tyon CSS is that the input is actually not styled at all so let's change that and give it some class names so let's give it a class name of border and a padding of one if I now save that and go back you now see that the um input is much more defined also I can change the color so we can give it in border of let's say gray 800 and if I now save that and go back you now see that the border is much more pronounced and the put itself is much more pronounced so the input looks good I now want to have an button which will be a submit button so let's give it a type and the type will be submit and let's also give it a name which will again just be submit I now get an error if I hover over type it tells me type submit is not assignable to these types and that's because I have uppercase submit while it should be uh lowercased if I now save that and go back we now have an button I guess even though though it does not look like a button and that's again because of default TS styling so let's change that let's give it some class names so let's give it in class name of BG green 500 rounded which will be large um margin top which will be two text which will be white and padding Y which will be two if I now save that and go back yeah this button now actually looks like a button so I think that looks quite good and now why do I use this type submit right now well that's because I want to uh have this button as our submit button so this button is inside of the form and if I give it in type of submit that's the button which will submit our form and right now if I write something inside of my input and submitted you will actually see nothing um that's because we have this form right now as in get request and that's why you see right here inside of the URL this input which equals our input that's because we made a get request and not a post request that's what we want to do with uh server actions so let's change that now and actually create our server actions so that we can submit our um to-dos to our database and now the fun thing with server actions or the cool thing is that it's actually very easy to do it's actually just an async function if you think about it so how do you do how do you create a um server action so let's go right here outside of our export default function and let's just create a async function we'll have to give it a name so let's just name it create and then I just open this function so now this is still not a server action right now it's just an async function and to make it a server action we have to make uh we have to use this keyword which is called use server and with that I now use server actions so we just use a normal Asing function mark it as use server and with that we create a server action which exclusively runs on the server and never on the client now one more thing I want to do is get through the params form data so let's get form data I now get an error and it tells me right here form data is declared it's never used that's okay but parameter form data has a type of any so let's give it in type and the type will just be form data now we have types and no errors anymore now what do I want to do inside of the server action well I want to get the input and post it to my database if I now go back that's the input and I want to get the message which I write in here and that's actually quite easy I get this input through my form data so what I have to do is create a constant let's name it input and this will be equal to form data and now I want to get something from my form data and for that we have created a name inside my input and I want to get now um my input based on this name so I can do now a form data. getet and I want to get something based on the name of input now with this I just get the input so the message which I write inside of this input element with the name of input I could also change the uh input name to hello and then I could get this form data through the name hello but I don't want that let's leave it to input that looks good so now I think we can actually post our data to our database so for that let's do an um await Prisma and pisma has to be imported from our DB file not from Prisma client um do too do create so let's create a new record and I have to pass some data and I have to pass the input and the input will just be equal to our input now I get an error right here and it tells me type null is not assignable to type string so let's give our input and type of as string now I get no errors anymore and that looks good so still it won't work we now have to actually um connect our Asing function or our server action actually to our form and for that I will now use this keyword action or the property action and the action will just be equal to our create function so now our form is actually connected to this um server action which is called create and we can actually test out if it works so let me go back let me delete this gibberish and let's say um go get uh some milk if I now click on submit yeah well you don't see anything right kind of weird but um that's because actually we don't render right now our to-dos so let's do that so what I want to do now is get my data from my database and then just render it uh down below so on top of my export default function let me just create a async function called get data and let me open that function inside of here let me do an constant data which will be equal to await Prisma do too um do find many since I want to find many to-dos and let me also select a few things I only want to get the input um so let's do input true and I want to get the ID so ID of true now again also if you can remember we also have this created ad property and I want to know all my uh to do by the created at timestamp so let's do an order by and I want to order by created at and it should be descending that means that I want to get the most recent to do first and then uh all other ones um based on the date I guess now right here we now write our query let's also return that data so let's do in return data now I also have to get it right here in my server component so for that I will just do in const data and this will be equal to aate get data now I get an error and the cool thing is it already gives me in fix did you mean to mark this function as async and yes I will have to mark it as async since I want to use it right here as in server component and fetch data so this will be now an export default async function so now we have the server component I and I get also data so now I think let's map over the data and show it so let's go under our form element right here and let's create a new div element let's give it in class name and this will just be margin top of five Flex Flex which will be call and GAP y of two now inside of here I want to map over my to-do so let's R in data do map let's get the to-do and let me return that now inside of here I can now just uh simply return a P tag and let's just say um to-do do input if I now save that we still get an error and it tells me missing key prop well yes that's an react error so let's give it in key prop and the key will be too. ID now why do you have to give it in key that's just uh to prevent any hydration error messages inside of the console if I now save that and go back you now see our message go get some milk now let me create another to- do let's just say go shopping if I now click submit yeah I'm waiting wait nothing happens well that's quite weird right because if I now reload the page you will actually see our response right here go shopping but why did it not revalidate automatically well that's because we have to revalidate our cash you can actually also see that if I now open my network Tab and create a message so let's say again hello how are you and click on submit you right here see actually that we create a post request but we don't invalidate our cache which means that we don't see the message right here so what we have to do actually is uh use a handy function provided by next which is called revalidate path so under this a Prisma to-do I want to now use this revalidate path function provided by next cache and you have to import that right here so import revalidate path from next slash and now I have to pass a URL which I want to revalidate and since everything right now happens on the index page I want to revalidate our index page which is just the slash symbol right here if I now save that and go back and let me change it to let's say um go bu some shoes and click submit you will see it will automatically revalidate for us and we see the to-do automatically without doing anything or without even refreshing the page so now we see everything works so now you see we have now created created a very basic server action we just create a Asing function we give it a name we pass our form data through the props to get some inputs we also explicitly mark it as used server with this used server keyword I tell react that I want to make it a server action and not just a normal async function which runs on the client then I get the input from our form data I then do some work with Prisma to create a post request and at the end I use a handy function provided by nexts to revalidate my um cache I guess so again with this revalidate path I just tell next shares hey we validate the cache for this URL if I would now be on the URL I don't know/ ABC I wouldn't use just a normal slash right here but I would use slash ABC since this would now be done the correct URL but since we use the index page this will just be the normal slash now if I go back right now this is just a normal pag but I also want to use a um delete button and I also want to use a edit button so that the user can uh edit the to-do and uh make it something else I guess and now to do that that's actually very simple let me delete this pag I won't need it anymore let's create a form element I won't need an action right now so let me delete this action let me give it in key prop and this will just be a too. ID let's let me also give it in class name which will be Flex now inside of this form let me create a input and the input will be of type text let's me give it a name and the name will just be input and I want to give it a default value and the default value will be to-do do input now again let's also give it in class name since if I now save it and go back you will again see we don't see any styling uh even though the input works we don't really see that that's an input so let me give it in class name of border and Border or not border but padding of one if I now save that and go back now you see that this is actually a input and that the user can click on it now let's create two buttons one to save um our input and one to delete our input so for that go under your input and let's create a button um let's say save and let's give it a class name which will be of Border let's give it in BG of um green of let's say 400 and let's um leave it like that if I now save it and go back you know see a button with save that looks quite all right let's also create a delete button so under this button let's create another button let's say delete let me spell that correctly and let's also give it in class name which will be border and let's also give it a BG of um red and let's save 400 if I now save that and go back back we now have two buttons one is save and one is delete now still both buttons don't work if I click either one you see nothing actually happens so now what do I want to do if I uh submit this form so if I click the save button I want to save my input so let's give it a type which will be called um submit since I want to submit the form with this button now for the delete button we will do something else but for now let's just leave it like that and let's just give the save button a type of submit now again what do we have to do we have to create a server action so let's go on top and right under our um async function create let's create a new server action and let's uh name it async function let's give it a name of edit then inside of the params let's again get the form data which will be of type um form data and let me open that f function now inside of here again let's give it the keyword of use server since I want it to be a server action and then inside of here let me again again get the input so let's do in const input which will be equal to form data. getet and I want to get the input now there's one thing right now if I want to update a to-do so right now I want to update a specific to-do I also have to get the input ID of this um input message so to do that uh let's create another input right here and this will now be an input of type hidden and let's G let's give it a name of input ID and let's give it an value which will just be to-do do ID now with this input hidden I can now get this uh value so this to-do ID through my form data so let's create another constant let's name it input ID which will be equal to form data doget and this will be equal to input ID I can now do my Prisma query so let's do an await Prisma um. too. update since I want to only update a specific to-do and then inside of here I have to say where that is and the where is equal ID to our input ID I now get an error and it tells me again type null is not assignable to type string or undefined so let's uh give it right here as string and also let's give our input a as string so now what you have to do I also have to pass the data which I want to update and this will just be right here data and then right here we'll just have the input and the input will be equal to our input now let's save that let's also use the revalidate path function since I want to revalidate my cache and the revalidate path function will again just get our index page which is an empty slash I can now save that and we now again have to create a connection between our form and our server action so let's scroll down and let's give our form a action and the action will be equal to edit so now if I save that and go back we can check that out if it works so instead of go buy some shoes let's say go buy some um I don't know what can you buy um some backs I guess and let me save that so now it has revalidated automattic ially and if I reload the page you still see everything keeps persisted I can again change it to let's say again shoes and if I click on Save and reload the page it's still the same which is go by some shoes so now our save button fully Works which is great now let's make our delete button work so for that we'll create another server action so let's do an aing function delete and let me open that function now you see I get an error and it tells me identify expected delete is a reserved word that cannot be used here that's correct you can't use delete as an a name for a function since that's in reserved keyword so let's just change it to delete item I guess that is also fine then inside the params let's again get the form data and this will then have in type of form data yeah that looks good and then inside of here again we have to explicitly tell react that we uh this async function should be a server action so let's use the keyword which is use server and now what do I want to do inside of here I want to get our input ID and then delete our to-do based on the input ID which is an unique identify so let's create a constant let's call it input ID and this will be equal to form data doget and I want to get the input ID now what I want to do is an ARR Prisma do to-do dot delete and I only want to delete a singular to-do that's why I use delete and not delete many then inside of here I have to say where that is and where will be ID equal to input ID now I again tell get an error because it does not know if it's in type string so let's right here do an as string so that looks good now the last thing I want to do is again revalidate my cache so let's do in revalidate path and that's pass our index page now again we have to create a connection between our form and our server action but now if you can remember it won't work why that's because we already have an form with an action and a button of type submit and I can't have two actions inside of this form element so what you can do is a nice little trick and give our button so delete button a form action and the form action will be um the leete item and with that I don't have to create a separate form but I can make everything work inside of one form and I can just then give this button a separate form action so now in theory this should work so let's go back and let me delete this right here go get some milk and you see it has automatically revalidated so the cash got automatically revalidated and if I reload my page right now you see it has been actually also deleted from my database you can also see that in the network tab so let me open my network Tab and let me delete right here go buy some shoes if I delete it and click right here you see we made a post request to delete our item from our database so we now basically understood how to use server actions you create a async function you give it a name um you get the form data through the params if you need it you don't uh necessarily need it if you don't want it you have to now now that's very important you have to to explicitly mark this Asing function as use server because if you don't mark it as a new server um react will just think that this is a normal async function which should run on the client you don't want that you want this function to explicitly run on the server so that you can at the end use a server action then inside of This Server action you can do whatever you want so uh get something through form data manipulate something or I don't know console lock something if you want to do that and then if you want to use an handy function to revalidate your data you can use this revalidate PA function provided by nextjs and with that you can then revalidate the path so revalidate the cache to see the new result that you have just posted to your database for example so this is the very basic example of how to use server actions but now there's a thing right we create a uh to-do right here and if I click on submit you get the um result right here as you see but now there's a few things I don't like first of all when I clicked on submit it it did not show me a pending State also it did not reset my input right here it should automatically reset my input once I click submit also if I now click on delete I don't see any pending State and if I change my to-do right here to some gibberish and click on Save I again don't see any um pending state so let's actually check change that and let's create a a better version of this example right here where we show um the pending state where we show uh or where we reset the form actually where we do error handling and um yeah so let's actually do that right now to do that I will actually create another route so inside of the app folder let's create a new folder called beta and then inside of this folder I will create a page. TSX now inside of this page. TSX X I will create a better example so that you can then compare both examples so what we will do right now is go to our page. TSX and let's just copy everything inside of this return statement so let me copy that and let me paste it now inside of here so first of all because I can just paste it let's create a export default function and let's uh name it better example and let me open that function right now inside of here I can now create a return statement and paste what I've just copied from our uh original page. TSX now right here I get a few errors that's fine we'll fix that in a second first of all what I want to do is get our data so let's again go back to page. TSX and what I want to do is just copy right here our async function which is called get data let's go back and let's just paste it right here on top then right here inside of our export default function let's a cons data equals to await get data again we have to mark our function as a sync so let's an async uh export default async function also right here let's import Prisma from our DB file so now that looks good we now get our data from our database and we still get a few errors but that's fine we'll fix that in a second now if you want to you of course can go to your normal page. TSX and just copy each server action and paste it inside of the page. TSX but I don't like this approach I actually would prefer to do it a bit differently what you can actually create is a new file and uh then just store all of your server action in one Global file where you can access this where you can access it then and in my opinion it's just a much cleaner architecture so what I want to do is go inside of my Explorer and then inside of my app folder create a new file called action. TS now inside of this action. TS I want to store all of my server actions so let's go to our page. TSX and let me copy all server actions so Asing function delete item Asing function edit and async function create I will copy them and now inside of my action. ts I will paste them inside of you let me also export uh all of these functions so export um function create Let's do an export function function edit and let's do an export function delete item let me also import Prisma right now from our DB file and let me uh um import the revalidate path function from next slash so now this action. TS file works as you see everything is imported and we don't get any errors anymore now inside of my page. TSX I could now just addit all of these um server actions so add import form uh do doaction let's also import edit right now from doaction and let's import thead item from doaction if I now save that and go back and go now to our new route which is called slash beta and go right here you will now see everything still works which is good now there's one thing I want to change right now we right now R validate the path for our index page which is called slash but I want to now we validate the path which is Slash better so let's change that for each server action slash better and also right here slash better that looks fine so now let's test out if it works so let's create an input of let's say hey how are you and let me click on submit and yeah you see it right here hey how are you so now we see that this actually works We R validate our data or cash and you see the to-dos right here now let's actually do the first thing which is create to create a pending State let's start with the save button so let me open my Explorer let's go inside of the app folder and let's create a new folder called components now inside of the components folder let's create a new file called save button. TSX and then inside of here I want to now get the pending State and then show that in the button so to do that let's create again an export default um function let's name it save button and let me just open that function inside of here I want to now return a button or actually let let me just go back and let me um copy this button and let me paste it right here so instead of creating new one let's just use what we already have so now we have this button and if I go back and just import that so let's import the save button and go back and save that and if I now create a message or change it actually so let's say hey how are we and save that reload the page you see it works so now we have just uh broken this button inside of a component but inside of this component I now want to get the pending State and now why did I have to break this button inside of a component and that's because we will get our pending State through a hook and this hook has to be hydrated on the client so we have to mark this file as use client so on top Let's do an use client and now inside of here we can import const we can the structure pending and this will be equal to use form status now again use form status and not use form State be sure that you import the correct one and it's imported from react-dom now we can use this pending state to actually get the message if currently our button is in a pending state or not so instead of save let me right here do intern so if pending is true let's do in question mark and let's say right here I'm saving dot dot dot and if this is not true let's use some colum and let's just say again save so now let's actually test out if it really works so let's first of all again create a to-do so let's say get some shoes let me click on submit and now let me change the get some shoes to get some um I guess backs and if I now click on save you should see right now the saving indicator and you saw that for a second I can now reload the page and still it's still there now let me again change it to shoes and if I again click on Save you'll see for a second the saving um I guess animation now there's one thing I don't like and that's if I click on the leete there will be two things first of all we won't see the pending state but if I also click on it you saw that the saving uh button uh had an pending State and that's not what I want I want that the save button only has a pending state if I actually submit the form using this button what I don't want to do is if I click the delete button I don't want to show a pending state for my save button I only want to show a pending state for my delete button and that's uh and the problem comes because if I go back to this uh page shot TSX inside of my better you will see that both buttons are inside of the same form element and even though I did it at the start just to save some time and to be a bit faster this has the disadvantage that both button are child components of the form which means that both buttons get the same pending State and that's of course not what I want so to circumstance that what we have to do is actually create two separate forms and then with the two separate forms I also get two separate pending States so what I will now do is wrap my form right here inside of a separate diff and then this diff will now have an key and the key will be to-do do ID I can delete now this key from my form and now right here we have one form let me create another form right here and then for now let me delete the action and now inside of this form I will just have a button which is delete so let me paste it inside of here and now if you can remember if I go to the action. TS inside of our delete uh server action we get an input ID and now if I go back right now in of this form we don't get an input ID so let me just copy it from this other form and paste it also inside of here so now we have two forms one uh form will have a save button and the input for the type text and the other form will have the delete button so now uh I will change that I won't need this form action anymore I will just need a type which will be of submit so let me do that and then I will give this form a separate action and the action will now just be delete um item so now in theory if I go back let me again create a item hello how are you and click on submit um you'll see that does not look good so let's give this diff yta class name which will be um width of full age of full let's say flex and items of Center if I now save that and go back yeah and that now looks better so now let's test it out so if I change this to um hey how are we I guess and click on save this still works we still see the pending state if I reload the page we also still see the message but if I now click on delete the pending State Should now not be anymore available to the save button so let me click that and here you saw now the save button does not have any pending State anymore so now let's do the same uh as we did for save button also for the delete button so let's go inside of our components and let's create a delete button. TSX Let's do an export default function let's name it delete button and let me open that function now again let's go to our page. TSX and let me just copy this button right now and let me uh do a return statement and paste it inside of here now again what I want to do is get the pending State and for that our component has to be a use client component so let's mark it at to on the top as use client and now right here I can now get the uh hook so the use form status hook and through this hook I can now get the pending state so let's do in constant let's the structure pending from this object and this will be equal to use form status again it should be use form status and not use form State use form state is something completely different and we will use it uh in a few moments but for now let's import use form status and that looks good what I can do now is delete this delete text and let's again use C Turner so if pending is true let's return um deleting dot dot dot and if this is false let's use in colon and let's just say delete if I now save that and uh actually go to my page. TSX and instead of this button let's use our delete button component and if I now go go back and create a new input or a new to-do I'm sorry so let's say hey get some milk and if I click on submit we now got our input right here with the two buttons so let me again change it to hey get some bread let me click on Save yes we get the pending State and we don't get the pending state in the delete button and if I now delete it we should not get our pending state in the save button but just in the delete button so let's do that and you saw that worked perfectly now again we're still not done there are still two things that that I don't like first of all our submit button still has no pending State and our form does not reset so let's change that and for that what I will actually do is break this form right here inside of a client component or break it into a client component so we will open our Explorer go inside of our app folder inside of the components folder and create a new file called form. TSX now again inside of here we'll have to do an export default function which will just be called form um I guess element and let me open that function inside of here let's create a return statement and then I will just return this whole form so let me copy this form and paste it inside of here now inside of here we get an error and it tells me uh we have to import create so let me import create from our doaction and now if I go back to my page. TSX I can now use this component instead of this form so let's import our form element from our components now still everything will work we right now just uh broke our um I guess our normal form inside of a component so if I would go here and write something and click submit you'll still see everything works perfectly but I think the first thing I want to do now is actually uh make the reset of the form work and that's actually very easy so first of all I have to mark this component as use client and that's because I want to use an ref so the use ref hook provided by react and to make it work it has to hydrate on the client side and that's why I have to mark this component as use client because if I would not do that we will get an error so now let's do a constant called form ref which will be equal to use ref and use ref is imported from react and let's give it an initial state of null then what I want to do is give the form a ref and ref will just be equal to our form ref which I've created right here so what I want to do is with the ref is when I uh use my action or if the action runs in that sense I want to reset the form using my form ref so I can then call form ref. current. reset and for that I will change it from right here create so I right now pass it already in function but instead of uh passing a function I want to create a new Asing function inside of here so a new async Arrow function and let me open that right here and then inside of here I can do await create and you will see we get an error and it tells me expected one argument but got zero an argument for form data was not provided so through the params right here we can get now form data and form data will have the time uh type of form data so now I can pass my form data and everything will still work but while I want to do now is after I called my create function or my create server action I want to also reset the form and I can do that with form ref. current. reset now you saw we didn't get any types and that's because I have to pass use a generic so let's pass it a generic right now which will be HTML form element now you see we get the types so if I delete again this reset I can do an Dot and now I get all of these options so let's toggle reset and now that looks good so if I now save that and go back uh you see an error wait why do we have an error everything worked a second ago well I was expecting this error because that's uh because we still haven't added one thing so let's first of all go through this error the error tells us it is not allowed to Define inline use server annotated server actions in client components so what it means means is right now if I go to my action. ts it tells me that I'm not allowed to make right here server actions inside of in client component it also gives us a very nice solution which is to use server actions inside of a client component you can either export them from a separate file with use server at the top or pass them through the props so to make it more basic to generalize it what it tells me uh as a solution is just to mark this file so this action. TS as a use server file so if I go on top I can just mark it now as use server and if I now save that and go back and reload the page you will now see we don't get any error anymore now let's test out if everything works so let's get say again um get some milk and if I click on submit you see the form has been successfully reseted and that looks great now what I want to do is create a submit or a pending state for our submit button so let's do that let's go back to our form. TSX and now inside of my form. TSX what I can do is on top get our pending State uh so let's do in const pending so we D structure pending from the object and this will be equal to use form status and now what we can do inside of this button is just check if pending is true so if pending is true um let me just return um saving dot dot dot and if it's not true let's use some colum and let's say save now the interesting thing is that I already know that we will have an error but still I will show it to you if I go back let me reload uh to be sure that everything works and let's now say hey uh does this um have a pending State and if I click on Save we don't have a pending state so now the question arises why does that not work and I know the answer and the answer is that we can't use the pending state in line uh if the button is of type submit so let me make it a more a bit more generic so right now we have in form element within button which has Type submit and inside of this type submit button I want to get the pending State well this does not work because I have to break the submit button inside uh or to a separate client component so so if this button would just now be a normal button without type submit it would work but if it's a button of type submit I have to break it into a separate component now you can either do it inside of here since that's already in used client component or you can create a new file I will now just break it into a separate component inside of this file so above the return statement I will just create a new function called submit button and I will open that function and inside of here I will now just return this button so let me copy that and let me just return it inside of here and then inside of the form element instead of the button I can use the submit function component now it will still not work because if I test it hey and click save you still not see that and that's because I have to use this hook inside of my function submit button um component so if I now use this hook inside of the component and test it again so hey how are you you will now see a pending State you saw save dot dot dot so you see now it fully works and again why is that that's because always if you have a button which is of type submit you have to break it into a separate component and this component also has to be a used client component so now you see that works perfectly which is great and we can delete everything I can change that again to shoes save that and that's great now there's one thing that does not work and that's error handling if I now go to my actions and let's say I just had an error and instead of Prisma I said and Prisma with multiple a if I now save that go back and create a new I guess to-do and click on Save we get an error but we don't get our custom error but we get the default next sh error we don't want that what we want is to just uh send our own custom um error message now again that's actually not very hard to do so what we'll have to go uh what we'll have to do is again go to our form. TSX and now right here under our form ref we will have to get a new we will now have to use a new hook and this hook will now be the use form State hook with the use form State hook I can handle errors and show them in the UI so what you'll do is use an constant and inside of here we'll now uh pretty much set it like a normal state so we'll have a state and let's also do in form action and this will be equal to use form State not use form status but use form State now this use form state will actually get two params so first of all we will get the function so our server action which is called create and we'll have an initial state so let's pass our server action called create and our initial state is just null now right now we get an error and that's fine we will um change that in a second so now we right here have created a hook called use form State and then inside of here we have in getter and a Setter so with the state I just get the current state or the message in that sense and with the form message I can then just create my um I guess action or post my request to my action and then I have my use form State hook and then I have to pass two params and that's first of all the server action and then the initial State now we get an error right here and let's change that and to change that you have to go to your action. TS and now this is interesting right here in the params I now have to get one more thing which is the previous state this will now just be any and if I now save that and go back you will now see that the error has gone and now again you have to do that since we have a initial state of null and I want uh to Now set it to the previous state you don't really have to understand why that is I myself I find it a bit complex but you will just have to pass a previous state you also don't have to use it inside of here just get the previous state through the param set it to any and then it will work perfectly now still we get an error right here um if you scroll down you right here see array create and we see in red squiggly line well that's because you now don't want to call it the create function directly right here but you want to use the form actions so let's delete this whole arrate and let's just call form action and let's pass our form data inside of here so let me do that form data and now you see we don't get any errors anymore so that looks good what we can do now is go to our action. TS and what I want to do now is create a try catch statement and then if we catch an error I want to return a um a error message so let me wrap everything in a try statement so let's do in try and let's do also catch we'll get a error let me open that and that's fine now inside of our CH statement let me copy everything so this will validate path right to the input and let me paste it inside of the try statement that looks good and if we catch something so if we catch an error let me just do a return and let's say failed um to create if I now save that and go back I want to now show our uh error message so under the submit button I can now get my state and let you as string so now if in Fury we should now uh catch our error through this try catch statement then I will return this message and we should see it through our state right here so let's test it out let's say hey do we see the error let me click on Save and yeah you see failed to create you can also make it more pronounced so we can uh wrap it inside of a pag let's go in class name of text red 500 and let me paste it again and let again try to create something or let's first of all reload the page and then let's say again hey how are you let me click on Save and you see fail to create so our error handling now fully Works which is great let me now again spell pisma correct to see if everything still works so um new too let me click on Save and yes it works perfectly so now you have learned everything about server actions you have learned how to create server actions you have learned how to handle um the pending State you have learned how to handle errors or in that sense also how to catch them and show them in the UI and you have also learned how to reset the form so now you know actually everything about server actions and now the last thing we can do is actually push our code to GitHub and then deploy it to Vell so let's do that right now so I'm now at github.com / new so let's now create a new repository name let's name it next sh as 14 server actions dyt let me make it public and let's click on create repository now right here you will see instructions how to create our git repository so let's do that right now so now before we push our code to GitHub what I want you to do is open your dog ignore and then inside of here you will add your EnV that why do we do that that's because I don't want to uh deploy my EnV to my GitHub uh public repository so I don't want uh people to see my environment variables so what do we do now we open our terminal inside of here let's do a git init this will initialize a new git repository let's do a git um add everything within dot so get add dot then let's do git commit dasm and for the message let's say finished project let's click enter and then if we go back to GitHub let's right here copy this GitHub Branch M main let's paste that inside of here let's copy the remote origin let's copy that and paste that and then let's push our code so let me copy that and paste that if uh you now see we now push our code to GitHub and if I reload my page at GitHub right here you now see the code which is great so now let's go to yourell and deploy our code so I'm now on verell and you can now just import right here this um project which is next shes 14 server actions so let me click on import and now before we click on deploy right here in the environment variables go back to your EnV uh file and copy the database URL copy that and then paste it inside of here now you can click on deploy and as you will see now the project will be deployed to Vell now if you want to see how the building process looks you can open right here this building uh Tab and you will see how that looks so once that's done I will come back back so not even I guess 20 seconds have passed and we already get an error and right here we see an error Prisma has detected that this project was built on V cell which Builders or which caches dependencies this leads to an outdated Prisma client because prisma's Auto generation isn't triggered to fix this run Prisma generate blah blah blah so what you can do right now is uh follow the URL and I've already opened that so since we use verel verel automatically cashes dependencies which leads to that the Prisma client's autogeneration isn't triggered and to prevent that what you can do is right your scroll to solution and we can use a custom post installed script so let me copy this post installed script let me go back let's open our package.json and then right here inside of our scripts under lint I can just paste that inside of here now let's open our terminal let me clear everything let's say get add everything within dot get commit dasm and let's just say added Prisma script and then let's click enter and let do a git push this will now push our code to GitHub and ver cell will automatically rebuild our project so our application has now been successfully deployed so let me click right here on domains and now we are on the original example so let's go to SL better to see the nicer example and now I'm on/ better so one one thing you should know before we actually test it out is that this project is completely static so we didn't add any revalidate Texs or anything this is completely static and only works with the caching so we actually with this revalidate path function we just manipulate the cache to reset it and this is why this example is very fast because we don't refetch anything and everything is always static so now with that out of the way let's test out this example so let's create a new to-do let's say hey get uh some shoes let's click on Save we see our uh pending state right here and this takes a bit that's because we use serverless function and we have some cold starts now let's change our uh to-do to hey get some milk let's click on Save we have the pending State and we have now hey get some milk let me also delete that and as you see it has been deleted so that looks good let me also change that let's click on Save we have the pending state if I reload it it's also still persisted so that's great so now you see our project is fully working so this is very nice and I hope you could also learn a lot from this video and if you enjoyed it please like it and subscribe and with that out of the way I hope I see you on the next video so now bye
Info
Channel: Jan Marshal
Views: 8,631
Rating: undefined out of 5
Keywords: nextjs 14 server actions, nextjs 14 tutorial, nextjs 14 server actions tutorial, nextjs 14 typescript, nextjs 14 reset form, nextjs 14 pending state, nextjs 14 handle errors, 2023 react tutorial, nextjs 14 api routes, nextjs 14 mutate data, nextjs 14 todo app
Id: T-G_wfL5MsM
Channel Id: undefined
Length: 65min 18sec (3918 seconds)
Published: Wed Nov 01 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.