React Hook Form (+ Zod & TypeScript & Server Errors) - COMPLETE Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
if you're creating a website or web application sooner or later you're gonna need some input from the user and we use forms for that imagine a healthcare insurance type of web app they may have dozens or even hundreds of inputs or government websites or even booking a plane ticket in those cases you deal with big complicated forms and you need a robust solution for that and one of those robust solutions is called react hook form so in this video we're going to look at how that works including with Zod for validation and including server side validation but before we do that let's just start looking at a normal form without any additional stuff so without react hook form so I just have a simple form here it has an input for the email it has an input for the password and then also an input for confirming the password and then a submit button so right now these inputs are so called uncontrolled inputs meaning if I type here it will work we can actually see text but this is not coming from react State these inputs just have their own internal state in the browser so we are not controlling it therefore we call it uncontrolled typically in real world people do often make these controlled inputs so here you would create a state variable for example for email two more for passwords and set password and confirm passwords but when you use these react hooks that's only possible in client components so I'm going to convert this into a client component right I have separate videos on nexjs I also have a whole course about reacts and xjs and then we want to make sure that this email value is actually coming from the state here so not its internal State like now we actually want to make sure it comes from this state here so we're going to say value is email same for the password confirm passwords so what we need to do is whenever we type here we do need to update the state so that means we're going to add an unchanged listener put in same for password and same for confirm passwords now these inputs are so called controlled inputs because we are keeping track we are controlling the input values ourselves the main benefit of doing this is that we have one source of Truth we always know where the values of the inputs are now coming from so I put in some gibberish right now if I now try to submit the form we actually already get a warning message here we're telling the browser this input field needs to be an email so the browser already offers actually pretty sophisticated client-side form validation so let's actually use that we are using type as email and type as passwords here but there are other things as well for example we can specify that required should be true so we cannot leave it empty and actually in jsx if you have a Boolean like this you don't need to write is true just specifying it will automatically make it through so if I save here and now if I remove this I leave it empty and I try to submit we actually also get a warning let's do that for the other fields as well so I will add required for those as well and there are other ones as well for example maybe the password needs to be at least 10 characters long and we can also specify a maximum length let's say the email can be at most 50 characters long so I will just type one two three four five if if I not try to submit you can see we get this warning from the browser so let me remove these all right so now we just have some very basic validation if I try to submit you can see it tries to submit the form and by default it actually tries to submit it to some URL that you specify in the action attribute now typically we want to submit it with our own JavaScript so you would do something like on submit right and then maybe a function called handle submits and then we can do it inline here or extract it into a separate function usually called handle submit and then here we get access to the event objects and here we will send the data to our server so I get a warning from typescript here because it doesn't know what type this is that's the downside of extracting it into a separate function if I would just do it in line like this typescript can already infer what this event object is going to be because it has the context there here I'm just defining a separate function right I could be using that anywhere it doesn't know that this is connected to this unsubmit so it doesn't know what this type is going to be but here it knows the type so here you need to specify the type now I don't know what the type is going to be so what I can do here is I just hover this and I can see it's going to be some react form event so I can just copy this type this like that so typically you're gonna see either prevent defaults if I save Here and Now type out the form with some gibberish but still conforming to the foundation you can see when I submit now it doesn't do anything it does refresh basically nothing is happening now the form also doesn't get reset so now we have prevented the default Behavior essentially when the form is submitted so now we can do it ourselves so here's where we would send it to the server right to do submit to server let's say right so then we would have our code for doing that we'll look at how to do this later in the video let's continue with doing some other typical things here so typically we also want to keep track of the submitting state so while it's being submitted while we're sending it to the server it should be disabled now typically you would create a whole separate use States variable for this so we could do something like is submitting initially it's false but once we have clicked submit we want to set it to true and then after we submitted it to the server we can set it to files again so then we can use is submitting to show like a loading spinner and so just some loading indicator essentially for now I will just use it to disable the button so the user cannot accidentally submit it twice so we can say disabled should be true if is submitting is true and then I can use two wins here I can Target the disabled state to make it a gray color so now what's going to happen is this is going to run very fast because we're not actually submitting it to a server so let me just mock a server call so I can use async awaits here and I will just quickly create basically like a sleeper expression here so it will just sleep for one second and then it will continue here I have to make sure you've mastered JavaScript itself as well I have a whole course on that so now if I'm just trying to submit something now if I try to submit you can see it becomes gray for one second because the submitting is true so it makes it disabled and with Tillman we can easily style that like that and then after one second we set it to false again besides the submitting State you should also have an error state so there could be errors so we could have errors set errors there could be multiple errors right maybe there's something wrong with both the email field as well as the password field so typically this is going to be an array when you use a primitive value for the initial value for the use State calls typescript will properly infer type because we have an empty string here typescript will probably infer this as a string however when you're using an array or object that's not the case so here it infers errors to be some array with never well that's not true an error is just going to be a string and we have an array of strings so we need to to properly type this ourselves so we're going to type this as a array of strings so I can do it like that right so you could also do this manually here but it's not necessary right and then string here doesn't necessary typescript can properly infer that but not with arrays and objects so when do we have an error here well for example when the passwords are not equal so before we actually submit it to the server we want to have some more validation here and specifically we want to make sure that the password is equal to the confirmed password it's not really easy to do that here with the native HTML validation so we have to run our own JavaScript here so here I get some suggestions which I will accept and we actually also want to return out of the function here right so if there's an error we don't want to continue and submit it to the server we want to return here user needs to fix it first before we actually submit it to the server we also want to set submitting to false again right so you can already see it's starting to get a little bit more complicated now because you can easily forget this for example alright so then we have the errors but then of course we also want to display them to the user that's another issue and how can we easily do that so that's not really a problem now because ideally if there isn't password issue we can display it right below the password field right but we just have an array of strings so this would already require quite a bit of programming to get a nicer structure here so we can properly match it with the appropriate input right so we're not going to do that here because this is just a bit too much work react hook form will make that much easier for us but let's continue here so let's say we do have some errors we just have some we'll just have one place where we're just going to Output all of them right so if that array of Errors is actually not empty we do want to display some error on the page so here what we can do is we can have a list of those errors so for each hour We're going to have an Li right so now if I try to submit something I say this password is one two three and this one is one two three four so they're not equal so now we get this error message here because here it's setting the errors so the state changes and here we render errors on the page one last thing we want to do here is very typically is also a reset the form so let me refresh here so after we submitted it to the server we want to make sure that it's empty again it's a bit annoying to do that now because we have three states here so we would have to do set email it's going to be an empty string passwords empty string they're all empty strings so now if we don't make a mistake and everything is actually correct now if I try to submit you can see after one second actually everything gets reset this is an example of a very typical form and look at how much boilerplate we have to write we have all these used days here we have this whole handle submit with e dot prevent default loading State error State and already starts to get a bit tricky as soon as you need to do something that's outside the normal HTML validation such as making sure that one field is equal to another field very easy to make mistakes for example you can easily forget to use set is submitting to false here it's annoying to deal with errors it doesn't have the right structure so it's difficult for us to match the errors now to the actual fields that were errored so this right now is not a robust solution especially again if you're working with large complex forms with dozens of inputs with various strict requirements for safety and accessibility so the most popular solution for this right now is react hook form you can see it's been growing pretty substantially and it solves a lot of those problems now there are other libraries as well like for example formic but it has hasn't been growing as fast some people claim it's not being maintained properly so this seems to be the the go-to solution these days so let's take a look at how we can implement this with and without zot so first we'll look at without and then we'll talk about why we even need some so they show an example here the main hook that you can import is use form and you can invoke it and what you get is register handle submit and then also form state from which you can get the errors so we have to register each input apply validation so it's using the HTML validation so it tries to mimic the HTML standards for form validation so it also has required min max the lengths that will look familiar to us right so now we're going to do validation through react hook form the other benefits of react hook form is that it's very low in size because it doesn't use any dependencies and it's also very performance which is really important if you have a large form and the reason itself perform it compared to what we were doing right now is because it doesn't re-render every time you type so we had a controlled form all our inputs were controlled that means if you type something you can see it's already renders on every keystroke because on every keystroke you have that on change updating the state so the state changes on every key stroke which will re-render the whole component including its challenge components however with react hook form if you're typing an input you can see the chance components here do not re-render every time and so it minimizes the amount of re-renders this is actually really important if you have a large complicated form it's also really fast at first mounting the form in the first place and has other optimizations as well so let's try achieving the same but now with react hook form react hook form will make a massive difference and I'll show you a before and after once we have implemented it so I created a new file now and now it's with react hook form I just duplicated it and let's actually install it first so I'm going to say npm install react hook form alright so it's installed now now what we can do is import the use form hook which is the main hook that you want to use and then we can remove all of this and replace it with a use form hook and what we get from this Hook is the following so we can this structure that so we got a bunch of things here so we can register every input we can handle the submit we need error State and loading state so we get that with form State here I get errors but we also want easy submitting we can destructure that right so in JavaScript we are destructuring form States here but it itself is also an object so we can destructure errors and is submitting from there and I get red security line here because I'm already using handle submit so I'm going to remove all of this actually we actually get a reset function as well and we also want to check if the password is equal to confirm password so we will use get values for that all right so we have this I'm going to remove this errors stuff that we did here because we now have a much better solution for that now we also don't want to have the value anymore we don't have to keep track of this ourselves we don't have controlled inputs anymore so we don't have to set this date ourselves with on change so we can remove that value and on change I still have disables here but let's just start from a clean slant and we have on submit here let me also remove that so now if I save Fair we have a clean slate now let's try implementing the same with what we get from reacts hook form so first you want to register every input so reactive form can keep track of it so what you can do here is you can call the register function that we get here and we can give it a name so this is going to be for email now when you call this function it will actually return props we want to spread the props on this input so you can use the spread operators so whatever is returned from here we immediately spread that on this input let's also register the other inputs so we have passwords and confirm passwords we then also want to do validation here in this register function call so I can say it should be required for example and actually I still had required to hear from the Native HTML validation let's just remove that and let's do everything through react hook form I can say required and then with a custom error message so I will just say email is required and it's the same as with the HTML validation so you also have Min length right and then you can say the value minimum five and then a custom error message right you have max length all of that works the same as with HTML validation but it's nice that you can immediately also give a custom error message so for email let's just make it required and nothing else for the passwords here let's make it also required but also let's give it a minimum value some minimum length of let's say 10 and with a message password must be at least 10 characters and here we will just make this required so now we have validation actually in place so now let's see what happens if I just fill this out with some gibberish if I click submit here you can see we actually get the same behavior as initially the browser trust is submitted so let's properly take care of this missing here we don't want to have a refresh or anything like that so on this on submit event this time we're actually going to use the handle submit function from react hook form so we want to call that function this function will immediately prevent that default so e dot prevent default so what we were doing here before react hook form already does that for us and then it gives us the data here but only if the data are validated so it will do prevent default but also immediately download the data according to what we put in here if it's not validated it will not give us the value here so once we do get the value here we can be rest assured the data has been validated properly we can run it in line like here but typically people do also extract this into a separate function and the typical name for that is just on submit that we can then Define here on submit so we get data so here we want to send it to the server we can just mock it for now with just a one second waiting here let's make this async okay so I get a typescript issue here so data is implicitly in any type typically you don't want to use any types in your code well how do we know what type is this going to be well remember because we have extracted this into a separate function Tasker does not know but if you do it in line like here typescript can can know better so here we see data is going to be with type field values so let me type this as field values as well so I will put this back here this is a type that we get from react hook form as well so I will just import that as well so let me import import type field values from react hook form or just just like this and this is just telling us it's going to be an object with you know pretty much any string and any value for that so this is basically the same as any or very similar to any to really properly type test we will use zot so now we have to accept that data isn't going to be typed very nicely so while it's submitting we want to give feedback to the user and we don't have to create State we already get is submitting here from use form so we can just use is submitting we can go to our button here to disable that disabled is submitting so now if I save here and I submit this and I'll click submit it actually doesn't allow me to submit this because I don't have at least 10 characters for the password so let me just type this now if I submit here you can see it is indeed submitting all right what about resetting the form after submitting well we also got reset from reactive form we don't have to do that ourselves we just call reset so if I save here and now if I try to submit this you can see it's submitting and then after that immediately resets the form and what about errors what if we have that error where we don't have enough characters how do we deal with that well react hook form actually gives us the errors here and the structures we really nice because for every field it will just have a key in that object So Below each input very granularly we can output a very specific error message so below the email input for example we can just check if that errors object has an email property in there and if that's true we can just output an error message just a paragraph and make it red text we can use the message property in there and I can do the same for the other inputs so here for password for example if there is an error we can use errors.password.password.message and let's also do the last one here errors.confirm password alright so now let's transmitting this with just some dummy data so now I have like six characters that's not enough so now if I try to submit this you can see we get a very nice error message here right so dealing with errors is much easier now if I fix this to more than 10 characters it doesn't have to match right now we'll fix that in a second now you can see we don't get an error and it properly reset right so now we have pretty much everything we want so now we actually also want to make sure that the password is the same as confirm passwords and we know not have to write any additional logic here and are on submit so we can write it immediately here with the other validation so here what you can also do if you have more complicated validation you have the validate option here where you get the actual value from that input and here you can say this value is strictly equal to the password value so this field if that's true we won't see anything but if that's false it will immediately return password so much must match so this will just create another error right so this is connected to this errors.confirm password right so if I do some test here one two three four five six seven eight nine ten one two three four five six seven eight is nine and then something completely different now if I submit here you can see we see an error message passwords must match so it's really nice because it's nice that here if you validate it and there is actually a problem here it's immediately connected to errors.confirm password so the structure of the errors is just much better so if we compare this with the previous version so here I don't even need to use State myself anymore I can just remove them import if we now compare this with what we had before you can see before we had Basics basically like a Frankenstein here of use States e to prevent defaults Setter calls you know some more additional Logic for validation randomly here in our handle submits but now you can see we don't even have one new state ourselves we get everything here from use form and our unsubmit logic is now much leaner and the additional validation is properly connected now with the rest of the validation the errors are much better structured we get the loading States very easily like this we can easily reset here we can easily get values and in here we get data only if it's properly validated so if it's not properly validated we don't get the data here so once we get the data here we can safely submit it to the server and if it doesn't properly validate it it will just give us errors which we can then properly deal with like what we've done here right so before and after you can see we had this ugly errors stuff here and yeah this is just much cleaner much better a much more robust solution now what's wrong with this why do we need even more stuff why do we need salt and now I don't want as well like yup these are all tools to help us build a schema and validate that schema that seems to be the most popular one right now so that's what we're going to use but they all work very similarly but why do we want to use that now the most important reason actually to use something like zat is because we don't only want to have validation on the client side we may also want to have validation on the server side so we are validating this form here on the client but then when we get the data on the server we basically want to do it again because you cannot really trust anything coming from the client on the server but the validation is going to stay the same the schema we can create one schema without and use it on both the client as well on the server if we use react hook form alone well that's just a form on the client so all the validation here that we're doing with required and Min length this is all just within this form here on the client but we want to do the same validation also on the server and maybe other places maybe we want to put this in local storage and then when a user comes back again we want to pull it out of local storage again when you pull something out of local storage it's also a good idea to validate that what you get out of it is actually the correct shape so basically whenever you have a boundary like an a like a network boundary between clients and server or local storage where you have when you can put something in local storage or out of local storage in a file system whenever you have a boundary like that and you're getting data in your app you want to have validation and with react hook form we only have client-side validation here with the form so with that we can create one schema and use it everywhere so we'll have one source of Truth for how our data is supposed to be and then we can connect that sort schema to react hook form so we're not going to use this react hook form validation we're going to use result validation but we can still connect it to react hook form so all of this will still work so if there is an error with validation it will just work with errors alright so I have duplicated our file once again and now we're going to add zot to the mix so let's install that we're going to say npm installs out result is its own separate thing but we want to connect it to react hook form here because we still want to use all of these features from react hook form so to do that we need a so-called resolver reactive form also offer first resolvers for yup and these other schema feather letters so that's from ad hook form resolvers so I'm going to install both of them alright so I installed that all right so let's actually import that here we only need Z from zot and then we can create a schema so basically the the shape of the form here and we want to use it not only here on the client side also in other places potentially so we're just gonna have a let's say sign up schema and we can say it's going to be an object so we say Z dot object and what properties won't have but it's going to have an email and that should be a string and specifically an email it should also have a password it's also going to be a string and then we can say it should have at least 10 characters right so if we look here we are now doing that with react hook form react hook form is using Min length value 10 and then we can also pass a custom error message we can do the same with sort so here we can say the minimum should be 10 and then you can pass a second argument where you can pass the custom message right so we're basically going to do all of that validation now not with react hook form but with zot and should also have a confirmed password so now we have the validation centralized in one place right so not only on the client do we want to make sure that the email is required also in the server just the fact that we added email here will already make it required right so now we can remove it from reactive form and the only thing we need to do with reactive form now is just register these inputs so here with the password when we say password is required we don't want to do that with reactive form we want to put that in our schema because we want to do the same thing in other places as well not just here in the form on the client all right so then here we can also remove this so here we also had a required and then we were also checking if it's the same value as the passwords here we can do the same with zones let's remove this so now we're only using react took form here for registering not for validation everything else stays the same is submitting errors handle submit on submit all of this stays the same but now we have our schema our validation centralized in one place one source of truth that we can then use in other places as well so how do we do that with confirming that this password is the same as this one in zat or inside you get a refine method so after object here we get all the data of the form here and we can just check if data.password is going to be the same as data.confirm password now when that's not the case we do want to show an error we need to connect that to some Fields here here we were using the confirm password so here to connect that with dots we do need a second option here and that's going to be a message passwords must must match but then we also want to connect it to some field so in some to use path for that and then it's actually an arrays because it could be connected to multiple inputs but here we only want it for confirmed passwords alright so this result this is not connected to react hook form yet so now how do we connect this with all the validation now to this form well when you invoke this use form hook you can specify some options here and we can have a resolver and specifically it's going to be the Zod resolver so we can import that from that ad hook form and then you can have multiple schemas you can also have sign in schema and in a real app you will actually have quite a few schemas so you need to specify which schema should be used for this form we can by the way also remove and get values we'll be using that for making sure that the passwords match but we're doing that now here in zot now we have connected jar to our form so now let's see if it still works the way we want it to work so if I do test at gmail.com and now let's say I have some passwords but the password is not at least 10 characters so let's see if I submit here you can see we actually get two errors the password must be at least 10 characters that's correct and passwords must match master I just put in some gibberish here so it's not the same and we actually get two errors the way that's supposed to work if I fix this one two three four five six seven eight nine ten so if I do the same here one two three four five six seven eight nine ten now if I submit you can see we don't get errors everything works everything is resetting everything still works the way it's supposed to work so now we have one central place for all the validation here with salt one more thing we want to do here is we actually can create a typescript type out of this and this is just a JavaScript runtime variable as it calls but we can also create a type out of this so we can say something like sign up schema and we can use zot.inver what it will take this sign up schema and basically create a typescript type out of that and why do we want that type well now we can also specify what the type of the form will be so we can use that so I can specify that here with angled brackets I can tell typescript hey this form is going to be of type signup schema why is this beneficial why do we need this well now for example if I try to register something here with the wrong name typescript will tell me this is not correct because according to your schema you only have email password and confirm password not email 53 if I remove this we don't get a warning here right so we add some more type safety this way and now when the data is properly validated typescript will also know that that data is going to be of that particular sign-up schema so not field values this is a very general almost like any type but now we can be more specific so now here if I do it in line if I hover e now you can see touch kit knows now it's going to be this email password confirm password object right so here we can just type this now as our signup schema now I don't like that this word time schema is almost the same as this one so I will add T here to indicate that this is a type I don't usually add I or t to my interface or types but here I think when you have a closed name I like to edit let me change this name here to T sign up schema and here as well I have a separate video on why you should prefer type over interface by the way and it's also part of my react next to escorts alright so now we have a pretty sophisticated setup here and we are properly validating here with Types on the client side now we may want to use the exact same setup here on the server or somewhere else so we don't necessarily want to Define all of this here in the form let me also remove this now it makes sense to put this in its own file and typically in your app you do want to have some kind of Library folder with utilities and one of the things you also want to have there is a file for the types that you want to use in your app it makes sense that you also put the schema of sort in there so let's just copy this remove it from this file and put that in here I do need to import salt here so let me import let me grab this from here and let me add that here all right so now we also need to export this so we can use it in other files export and now we can just import that here so let me import this alright so now it should work we should not get any errors in these everything is working again there's one more thing we have to do here which is actually submit it to the server and then if the server returns errors we also want to Output that on the page so we sort of need to integrate reactive form here with any server errors and we can use a result schema on the server as well so let's quickly Implement some Services functionality as well so I'm using the new next.js app router here and in there we can just create an API and this will be let's say the sign up route so that you can create a route file and here since it will be a post request because we're signing up somebody so it's going to be a post request we want to add something to the user resource in a database so here we got requests of type request we want to be able to submit the data to this route and then we can extract the data here the body we can say await request on Json and then we're going to validate that so this is where we're going to send the data to so let's actually try doing that so now we can actually send that it's not just mocking it so we can use Fetch and we want to await that and the routes is going to be slash API slash sign up right so this will send the data to this route here so then we can grab the data from body here and now we want to validate this again on the server because you cannot really trust anything coming from the client and by default touchgrid actually types this as any we actually want to type this as unknown because we don't know what it's going to be or at least we have to assume we don't know what it's going to be and now we can use our results schema that we already created we can reuse it now right this is our source of Truth so we can just use that sign up schema so we can say sign up schema I can import that and you can do two things you can do parse the alternative is use safe bars which doesn't throw an error if there is something wrong in that case you can just use result you can check if the result is successful or not if it's not successful we can grab some errors result now let's actually think about what we want to do here so here on the client when we fetch we get a response so typically you want to check for the OK property status is not in a 200 range so it could be 404 now how about we actually get errors that have to do with validation so we can just check if it has errors we can then use reactive form here to Output those errors so what we want to be able to do is something like this so we can just grab the errors there is an email property in that errors object we can set the error in react hook form and react hook form also gives us this function so let's actually input this from the use form hook set error so here we can specify for which Fields there is an error let's say here there's an email problem and then we just want to grab the message right so this is basically what we want to be able to do here on the clients I'm going to comment out the reset here because this reset will immediately remove the errors as well okay so now we can go to our route if we parse this according to our schema and there are there are errors so what we can do here we go over each issue that was found during parsing and we actually get a good suggestion here from Code Pilot so now for each issue we're creating these objects and then we can send that to the client we can check if this object is empty or not so we can do that with object.keys zot errors so basically it creates an array of every key if that's more if the length of that is more than zero it means there was an error so if that is true we will send an object with errors being the result errors and otherwise there was no problem so we will send an object with success as true don't worry if something is not clear okay so now how do we test this and maybe here we make a mistake so here we make some random number here so now if I type this out according to the validation rules so it will pass the client size validation so we get the data here so now if I submit here let's see what we get so it's going to take a second and we actually see an error message now coming from the server and saying something like expected string receives a number and that's because here of course we are passing a number here and it should be a string check out my react next to S course if you really want to become a professional react next to as developer in any case make sure you've mastered the underlying fundamentals both JavaScript as well as CSS I have courses on them both check out the links in the description thanks for watching hope you learned something and I hope to see the next one bye
Info
Channel: ByteGrad
Views: 45,605
Rating: undefined out of 5
Keywords: rhf, react rhf, react-hook-form, react hook form, react hook form zod, zod, react zod, next.js react-hook-form
Id: u6PQ5xZAv7Q
Channel Id: undefined
Length: 31min 20sec (1880 seconds)
Published: Sat Aug 05 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.