Building Great Forms with React Hook Form & Zod

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome this is my first YouTube video and I'm happy to have you here with me today I will talk about react hook form and building react forms in a maintainable and scalable Way by taking into account how multiple people would be working on a larger time scale on various form components so I will not just show you how to use a specific API or a specific part of the react with form library or something like that I will also talk about what makes code easy to maintain an easy to understand for a team composed of various developers with different skill levels so if that is something of Interest stick around if you have any questions leave a comment and if you enjoy the content consider subscribing and liking this video I am sure is going to help me out a lot okay having said that let's walk through what we'll build today we will build a sign up form a simple signup form initially and step by step we will extend its functionality to add more features and more capabilities to it and discuss while replacing various features and functionalities in different layers of the architecture now in order to provide us with an architecture that we can use basically a project to build the form into I set up a boilerplate if you will next.js application with tailwind and a component system that I haven't used before just for the heck of it because I want to explore and learn new things while I'm recording these videos so having said that we'll just jump into the code we'll take a look at the starting point which I intend to share in the YouTube comments or like description below the video and I'll just get started I'll start building the form and walk you through what I'm doing and why I'm doing the things that I am writing on the screen so before we get into the nexjs project let's quickly take a look at the final result we have the sign up page and on it we have a simple form we're not able to select to submit anything unless the form is valid if we add a valid information we are allowed to submit so if I make sure that I have the same password I'm able to register and I cannot press the register multiple times so it's only going to happen once now the back end is marked it doesn't remember anything there's going to be a couple of edge cases that we will hard code into the sign up API and see how we handle uh handle that so for example if I were to say Bob and press and send the same password on in I'm gonna get a different error from the server where the server is telling me that this email is not available so we'll talk about server-side error handling as well as client-side error handling foreign so let's talk about the startup project it's based on xjs it's not relevant that it's based on hjs because I'm only using next to have file based routing and to be able to talk about the application layer if you don't own xjs don't worry about it you'll be able to follow along just fine apart from next.js I'm using Del Wing I have just set it up and I'm adding react this UI as the component framework now this is the new component framework that I'm using I've never used it before until this project so if you have any opinions on it let me know in the comments below so far I've had a good experience with it I might use it in one of my projects I'm not sure but uh yeah that's that's what we're going to use and I think I'm gonna add the link to this documentation in the description of the video as well let's take a look at the folder structure and what we have in the project we don't have much so we have boilerplate pages one that just has hello world which is going to be a home page and the sign up page which will contain the form but right now it has absolutely nothing there's some configuration for Tailwind to work and there's this API file that is not relevant to what we're doing it's just going to have some edge cases I think right now I have one of the edge cases that we will tackle and I'll probably add a couple of other edge cases as we continue to build this form so having said that let's actually jump into it and let's start building our form let's start building some of the inputs and the button to submit the form using the component system that that we have and let's start centering everything on on the screen because right now the signup form starts right here to the top left and we probably want it to be on the center's area of the screen so if I add a little bit of CSS right here in line because this is not really the point of the video you we probably have better ways of centering things but for now I'll just place it three right here uh we're gonna have columns we're going to have some spacing between the elements and so on and everything is going to be on the center and it's going to be full height so right now we should be fine okay and let's see how we might add in input now I already know how to do this and since the the topic of today is not how to use react this UI I am just going to start copy pasting some code and walk through what is there so we'll start off with an input which has a color theme it has an ID and it has a type which should be imported from the easy UI and let's see what we get all right that looks acceptable so I can start typing in and things work fine let's also add an input and maybe a wrapper between before the input so we have this form field like entity on the screen so I can add an input for the email with the class label and so on and so forth and I also need to wrap this into a div so I'm going to wrap it like so with a div all right now I think the email should be correctly positioned at the top and if I click it I should be able to focus the field for helper text and in errors I think we need to use another label or something like that let's go ahead and do that really quick so I think I can just paste in a span which has a label text and then text error and it should work and by the way I know all those classes because I checked them in the docs you can you can go into DirecTV zy docs and see how those uh would work but in the interest of time I'm just gonna skip over some of those parts with our which are not very relevant to what we're trying to achieve today so if I if I were to see an error this is how it would look like so here's the point where we actually get started with the form implementation now if you are a developer that has to work and Implement and deliver this whole form you probably start thinking about how you're going to manage the values of the form the errors the state basically the internal state of this component before you send the data to your server and this is the point where react form steps in and helps us a lot let's start adding reactive form of the page and we'll start by actually looking at the docs and exploring this wonderful Library which I totally recommend you give it a try even if you don't pick anything else from this video this is really really worth your uh your attention so it's easily installable and afterwards it just gives us this hook if I use form which will contain pretty much all the API that we need in order to work with initial values resetting the values errors submission reaction to changes binding to other types of components which are more complex not just inputs or select and so on it is a really great library and we're going to start using some parts of the use form API right now so I'll just copy over this initial use form invocation and I will paste it onto the page here and of course I need to install react form so I'll go ahead and do that and again install and what was the full name react book so all right it's type safe it should come with all the the type definitions that we need so now I should be able to import it from reactive forms and we are ready to get started now if I want to use anything from the output form I need to bind some of those values onto my uh my content on the page and let's start off let's start off with the submission how might we enable this use form hook to tell us that we're ready to submit the form with whatever data we have there so that's like the simplest form that we might have let me get rid of that as well we have this handle submit and if I hover over it it doesn't help me too much it says you know it's a very specific type that we have right there but I know that these handles made can be used when a form is being submitted to intercept the default HTML behavior and to allow the reactive form library to intercept it and basically performance validation do additional checks react set some sort of States like flags for submission partition and so on um during that time if I have an unsubmit right here and of course this needs to become a form I am able to just pass it this handle submit and then on valid is the function that will get called if everything is all right in VR hook form so basically if I have this function right here and I log whatever whether or not I have successfully submitted the form I should be able to oh what's going on I should be able to uh have the first link between reactive form and our code base so let's see how that might look like if I name this from div 24 it's gonna be fine let's see what's the complaint from not from Port yeah so it's going to be warm and what do forms need in order to be submittable they need a button so let's add a button called submit and see what this gets us so we're gonna go back into our uh screen right here I'm gonna show the developer tools because we have an unvalid console log there if I click click submit I see that that code is running so yay we actually have handle submit uh integrated and the first touch point with vehicle form has been achieved now what about the values that we are submitting let's see are we actually getting something here so I'm gonna say we're getting probably some data of unknown type let's log it and see uh what's there if I press submit again I see that you know we are submitting a phone without any values at least from the perspective of reactbook form now how do I tell the active form that I have values inside my form now it would be great if by some miracle and I'm sorry Miracle but a little bit of magic uh the form the yakub form would figure out that I have an input with a an idea of email and uh it might use a this information to capture the value that I place in this input but that is not the case we have quite a simple API however we have this register API that we can use we use it like so we send over an object by spreading it so register returns us an object with a couple of values that are supposed to work on inputs select elements and so on so I can register a field of type email right here it's not going to give me an ID so if we hover over we'll see probably or like control click a couple of times we will see that this actually returns uh on change on blur or ref super important the name in Max and so on so it basically giving us some input props automatically onto the field let's go ahead and see what's happening now so if I press submit now I actually see an empty email key which is great because we've just made progress but what if we actually wanna type in some value so let's use this value and see if it actually works and it does so magically anything that we type here is now captured by react form now it's super important and this is actually something a little bit elusive but it's super important to note that if I were to console log here a message like sign up page running uh render function we might notice something interesting horses renders the first time but as we die in the email input there is no additional re-rendering or CPU Cycles allocated to the sign up page even though we're updating the input what does this mean well this actually means that the input is uncontrolled so it's actually quite quite important and something to keep in mind it's more performant this way because we are not going to be asking react to update or to check if there are updates to the entire home every time we type something into the field foreign now let's take a look at how we can hook simple field errors from the aquiform into our UI because reactive form also handles validation errors in a very nice way actually in the form State we actually have an object called errors and I'll just go ahead and log this this object entirely and here for the email I will say that it is required and we'll see what that gets us so I'm just gonna save that refresh the page and when I press submit we will see that sign up page is running the render function and that we actually get the required type and there's a message as well they're saying you know nothing and if I want to add a message I think I can uh send the string here email is required let's see if that does the trick and it seems to be working fine so this means that our red span Red Label here can become the actual text that we get in the message if the errors email object exists so this can become eternity operator where we have errors errors dot email if this exists and it has a message let's show that otherwise let's simply hide the input all together so now if I load the page initially I don't see any error if I press submit magically I have the error on the screen it says on error let's quickly fix that let's add the actual add-on message in here okay I'll just uh cast it to string and we'll revisit this just a couple of minutes later when we're gonna impose some type safety over the error messages and over the fields so for now I'll just say you know I'm pretty sure the message here is going to be a string because I'm only setting it as a string it can't be anything else let's see if this works so I'll refresh the page submit and the email is required and it's working uh just fine now let's extend the form by adding the password and the confirm password field and maybe start to talk about the separation of concerns a little bit because right now it's the first time where we're going to repeat ourselves and we might see something worthy of a separate component so if we go into the code base and we want yet another field we have to duplicate this whole thing so we have to do two of them one is going to be the password so I'll say password and I'll say password is required as a an error and the type is going to be password it's going to be the same thing for confirm password uh email here is going to be on firm password and everywhere else we're gonna change it to that oh and also we need a firm password as the field here because we're going to do a register for on password and we're gonna have a password field like so let's go ahead and do all that let's see if it's working so we have password config password when we submit we see validation errors for all of them I'm not going to worry too much about how consistent the messages are let me just see that they're working this is probably an easy one you probably already are screaming in the back of your mind we need to have a reusable component for this so let's go ahead and do that let's just copy paste this entire div let's go into SRC let's make an SRC folder so we have everything nicely organized this is going to be a usable component that text field or something like that so I'll make a components folder and I'm going to type it properly components and inside it will have a text field.tsx field like so input that and now we need to start to pick the parts that are going to come as props and the parts that are going to be here in the form so one of the things that is definitely proper based is the ID and the label of the field so I'm going to start making a interface for the props I'm going to say you know this text field gets props and one of the things you need to be the ID I'm not sure if this is required I think it could be optional I might be wrong maybe it will change our mind uh also there's going to be a label it's going to be a type string and I think we will require a label so let's go ahead and replace those it's going to be props Dot ID and this one is going to be drop start ID so I think we need the ID if we want to have this binding otherwise html4 is going to be undefined and you might have multiple IDs called done you find page so let's go ahead and do that and this is prompt label now another easy one is I think the error so let's abstract away the error message maybe has an object or a string let's go with the a string error is a string and it is optional so unless I send an error string I'm not gonna see I'm not gonna show the the span with the error message so it's going to be from that error equals whatever this was and this is a string I know I know for a fact this is the string so I can do that now we're left with this register and this is one of the the things that at least when I'm teaching react I find in the submissions from my students um some people send the register function as a parameter and I think this is wrong because if we send the register as a parameter we basically upgrade the text field from being a completely usable component so component it only works with reactive form and that is not necessarily the case maybe we will have a text field that has absolutely nothing to do with our our form Library so we will probably need to move this out of our component yet we would still need all the props that are coming from here to be sent over so one thing that we can do is we can Define the object called input props here so it could be props.input props in with props for now I don't really know what that will look like and I know it's probably gonna be optional and if it's optional maybe we can your operator like so there now once the intended usage of this input props well we're supposed to send the register like we did for normal inputs for more form so instead of sending the register as a parameter we send the result of the register as a paramet that way we don't need to know about reactive form into our usable component so let's do just that let's import our text field really quick onto the sign up page and place it at the top like so and see what we need so we need ID label and we'll take it from there so I'll say email label email and I think I can just oh not that that's the password I think I can remove the email field entirely and let's see what we get and what's missing so if I refresh the page we have the email it works oh it's like a password it seems so something to keep a note uh or the future and we get no email which is bad why because we never called register for the email so let me undo what we had there and see how we might pass this in as the input props if I say input props is whatever we got there I think we should be fine right let's refresh again press submit almost there almost there so we are getting the email we are I mean we are getting the email error but we're not sending the error back to our usable component and we're not seeing that message on the screen and if we were to add something like gbridge there uh we only get errors for the other two the password and confirm password and effective as submit it looks like I am okay I have access to the value from my input how might we send the error message where we already have the error key here so all we need to do is pass this in so it's going to be email and this is going to be a string that is it let's see if it works manifest the page press submit and voila or message our error message gets printed on the screen now let's also allow either text or password fields to be rendered by our component so let's go and extend the props to accept a type which is a type string and if type exists we're gonna set it as so otherwise it's going to be text so by default inputs will be text and if we want passwords we'll go ahead and subpute send the type as password and let's see so now the text field should be fine and if we want to have a password field all we need to do is replace this we should be buying so password is required and in a similar way I think the text field will be used for confirmed password as well okay we just need to pay care of the demo case thing for the register so it's confirmed password in one single word otherwise it might not work and then these two are confirm password all right let's see if this uh works refresh the page we press submit things are looking good now if I type in some information oh password income and password seem to be problematic because they're not hiding their inputs so we can say type is password right here now just to be a little bit more elegant and restrictive let's only allow password or text or maybe phone number or whatever was the other uh value that is available or type I'm not sure if phone number is actually part of the HTML spec anyway let me know in the comments below so now we won't be able to do something like bananas here and we should be good to go so email is with text and then password and confirm password are hiding their content like we would expect now there's one extra thing that we can do for the text field and that is to force the input props to be something that is returned from the register because right now it can be it can be bananas and we don't want that we would like typescript to complain saying hey you know you're not supposed to send us that you're supposed to send something that is similar to what the register uh who returns and we actually have two options here uh in a way we can take the typescript definition from the register function and send it over but if we do that then our text field needs to import types from the output form so it's no longer usable without it so I'm basically talking about the unknown from the input props or we can just Define it and duplicate basically the type definition which I think is probably the better solution here so I'm just gonna I'm just gonna follow along the type definitions and find something that is contained in the register returned object like this and I can go and place it into the text field so let's see what we're working with uh uh okay so so probably be like so and this is probably a function and this is also a function and they usually have events and I think that optional all of them so start graph callback is from react uh field name is actually a string and so on and so forth and let's see if we are allowed to uh use the text field with the new definition so it's complaining that ref callback is supposed to take a time so it's not it's uh it's not generic it's going to be an HTML input element I mean that's what we're going to use the wrap for we're gonna use it in the end on input and this input component itself will have a ref and that ref is going to be of an HTML input element it's a little bit more complicated I know usually these things happen very rarely on the project and depending on the effort you might do them or or not I would prefer to have them because this way I am not able to send something that is invalid here so I cannot say something not good equals true here and I'm gonna I'm gonna I'm gonna see an error saying hey you're not supposed to send this uh key onto the object so it's a little bit more safety a little bit more consistency in the code wow time flies and it's actually getting dark in here so uh I will actually uh turn on some more light let's see if that helps yeah this should be this should be better so we have our form we have some super basic validation and we're at a point where we can submit now we can go two ways we can either look at the submit function and how we link to the back end or we can take a look at how we work with the validation because our validation is quite messy and incomplete let's focus on the validation first so we would like the email to actually validate that the input there is a valid email or looks like a valid email as well as the password and confirm password we might want to take a look at the content and make sure that they are the same thing furthermore there might be a requirement to have a minimum amount of characters in the password and maybe a maximum or something like it has to contain at least a special character or a capsule or uppercase character or so on and so forth in order to address all these validation concerns we're going to use something called schema now schemas if you don't know are objects that Define constraints to validate or check other objects or array arrays so if I have a schema that says I am going to expect an email and a password and a confirmed password and the email must be of a specific type and the password must have at least five characters that entire definition that's schema definition can be used on top of what we get right here so if I were to submit a semi-valid form if you will I can use that schema over this object and get errors get validation errors from it in order to uh Define schemas and run validation uh logic with schemas we're going to use something called zot Zod is apparently a character from Comics as well oh the bad guy from uh from Superman actually did not know that interesting anyway Zod the library is a typescript schemable Edition um think so it allows us to Define how our form should look like in an ideal valid State and it will tell us if the current data that we have is uh valid it respects the constraints that we have defined with Zod or not so it's uh it's pretty easy to grasp there are other variants so we other Alternatives there's the Yap and there's also Joy or something like that so if you want to take a look there's also Yap romantian and I think there's a joy or something like that but that's more uh more focused on the back end so Yap or Zod are just great for uh schema validation so I'll just go ahead and install Zod and I'll start defining how our schema will look like and uh see how we can integrate Zod with Yahoo form so I'll uh quickly install Zod and can install Zod I don't think we need anything else apart from Zod in order to have the schema so I can import Zod and basically get everything that's inside the package like so and then I can Define my uh sign up schema as almost like a component if you will of course not not actually a component but similar in concept or components on uh the Declaration part of things not on the instant side of things so I'm not going to instantiate the new scheme every single time I render the form I'm gonna have it kind of like a component and only use it when I need to foreign if the zod's if the object that I want to create has a specific shape and it's actually an object I need to specify that uh like so so I can say Z dot object and it has a specific shape it has an email and that is a string and I think it's also an email and that's pretty much all I need to do in order to specify that object that I want to validate with this schema must have an email key that is of the type email then I have password password is again a string but this time I think I have a minimum of let's say six character as a maximum of uh 24 and let's leave it at that for now if you want we can take a look at how we might add additional constraints for the password so leave a comment if you like a video on that like how we might uh require special characters or the existence of an uppercase letter or something like that in the password but it you're probably going to get a point by the end of the video on how to implement that yourself and that is also the confirmed password which is of type Z string and it has the same constraints so we're not yet going to take a look at how we might validate the relationship between the confirmed password and the password just the individual field and now I can just take the schema and I could theoretically validate the data right here in the invalid so if Yahoo form says all good we could take the schema and validate our data against it but that is not uh the only thing that we can do because reactive form already has built-in support for these schema so we're just gonna build that directly into uh into our form we can have something called the resolver right here and we can either write on the resolver or use a Zod resolver basically a binding between this Zod schema and reactbook form which will allow us to automatically validate the values from the form with the schema that we send over plus bind and Link all the errors that our schema validation will provide so instead of us using the odd object here where we Define the required or mean or Max or so on we actually use the one that we defined in our schema so let's see how we might do that we will need the binding between Zod and the aqua form which is provided as a separate package it's called add hook form resolvers I think it's resolvers just like that not result and we're supposed to import uh I think it's going to be is all the resolver from this place dissolve the result are like so all right so if the install finishes successfully we should be able to just link these two together like so so we're sending the schema to be used as the resolver of this form and we can get rid of the validations from each register call right let's see how this behaves let's go on to our home page let's submit invalid email so we're already getting something else invalid email what's that where is that uh error coming from and then string must contain at least six characters so we already see the zot validation the schema validation working and going through the same path that we created for our errors in our text field so we're already integrating three entities we're integrated form we're integrating Zod and our component which behaves quite well if you ask foreign let's see if we can actually voice the email to also have a minimum size like so so now I think we'll get a string Mass contain at least six characters and uh so I'm not sure if I confirmed password is not working so let's see what's up with that con learn password that seems to be all right on firm oh so I had to type there we go confirm confirm great let's refresh submit all right now we're getting the message the proper error message on all three of them but what if we want to check the confirmed password in the password but if we want to see how uh the values of those two compare and show some error message if we want to uh Force the user to type in the same password twice well we have two options one of the options and I encourage you to take a look on the docs if you want to do it that way is to write another rule on the confirm password then check the value of the the first one the second approach is to have individual constraints for every field and then constraints for the entire object and this makes more sense in my opinion uh so I would like to define a constraint for this entire object so after every individual field has been deemed valid I will check that the whole object itself as passes all the validity rules that I Define so I'm going to define the password and confirm password validation as a concern of the entire object not about uh not related to just one field or the other in order to do that there is this refine method that we have with Zod which basically gives us all the data and we already see that we have three strings so if I were to add a pool here with Z string I would see it and the type definition so quite a good developer experience there if you ask me and if this is uh not true I would like to show a message passwords do not match where on the confirm password so basically on that field and let's see what this does so if I refresh the page and I submit it's all good then if I start typing something that is not the same notice that I see a password do not match only if the confirm password passes the individual validation but as soon as I uh start to have an invalid data or the field I have a priority and I showed the error message of the individual field not of the entire port so that's pretty much it so if I uh go ahead and reach the same value for both I am able to submit the form only when both of them are valid so if I type in something extra there and I press submit you will notice that I do not have an unvalid I need to correct the error and then I can proceed and submit the fourth all right so we're at the point where we're ready to send data to the server and basically proceed with a successful submission or handle of server-side errors now in order to integrate the API we have to talk about the separation of concerns again because this is the next point the next Frontier if you will uh related to uh splitting code and keeping things in separate layers it would be very easy to start adding a fetch call right here to a very specific URL so I know we have a API slash sign up endpoint right here which will simply says okay or it will say you're not supposed to use this email if you're using in Bob gmail.com if we add this fetch call into our form and let's say that this all up until now this has been called exclusive to our signup form the validation the logic on the client side and so on if we add this fetch call here then our form can no longer function without our running live back end that is probably not great why well let's say we want to showcase this in storybook for other developers to adopt in various other flows we won't be able to do that if our server is not running and usually in storybook you rely only on the client side it will be available it will also invite other developers to pollute the file with more business logic application logic that is not related in any way to the form the submission the error handling from the server and so on the lack of internet connectivity is not for example a concern of the form so it shouldn't be in the form itself so I am going to move everything that we have so far into a different component and then have this property this this callback have this function be prop base so have a property for the form to call when everything is valid and is ready to be submitted so we're going to have basically one entity that only knows about the form and then one entity that only knows about the data connectivity where uh to which endpoint to make calls to how to serialize in the serialize the data and so on from the perspective of the second entity the one that knows about the API how the form validates how the form shows errors how the form prepares itself to submit the data to make the data available is the detail of the implementation of the form component not the controller or container component knows about the apis and so on so that's what we're gonna do next and I'm going to go ahead and create a sign up form like so this is going to be an application layer thing while components is going to be a very usable very dumb uh folder so I usually have this convention where uh smarter components are either group together or initially they're just there in the SRC and then they slowly move into more specialized folders and this folder will have a sign up formula TSX which will contain pretty much everything that's on the page so let's start with that copy pasting everything I am updating the Imports and yeah the only thing that's different is the sign up page so I'm gonna do export cons so I'm gonna I'm gonna use a named export because I want consistency uh for all the component names in the entire application I don't want anyone to import this as something else not sign up form so I'm super specific about that and I'm going to imagine that I have a property for the unvalid I think we can call it on submit ready like we are ready to submit here's the data and we're gonna send the data to the outside world okay let's do that as props and actually we can just send it over here so we don't need to do anything with the valid data in our form we're gonna allow the parent to handle that for us entirely so let's let's clean this up a little bit I'm gonna remove that let's also add maybe a log a sign up header because we're going to need that uh we know later section and yeah I think that's pretty much it uh the phone will be completely concerned about validation and rendering and the API will be handled somewhere else for now I will just add it here on the page so I can remove everything else that's here we don't need the schema we don't need zot we don't need the text field don't need anything like that but we're just gonna have the sign up form and when the submit is ready we will do something with the data like so all right all right let's see if this works and fix any errors that we might have and take it from there and see how we might integrate the back end API so if I submit I get the same result as before if I pick some pasta data we're good and if I can submit I am handled submit ready that I see the the log right there with the value state we have all good for now what about this uh handling so let's take a look at the submission handling let's do handle submit so it's going to be 18 because we're going to do an API call and uh yeah just that step all right and data now it's unknown and know what the handle submit so now uh before we go into the API call there's something that we can improve upon in the type definition of the on submit ready and also on to the registers the register call because right now I can call any register I want so I can say bananas again bananas is okay but because I have this schema I would like if it's possible to only allow these fields that are defined in the schema to be used by the register call and we can actually do that and we can do that quite easily thanks to Zod so there's something called Zodi infer and it looks like this and we can send over the type so type of that and this can become a typing itself a sign up form of values now sign up for values if I hover over it you'll see it has email password and confirmed password the fields from here and I can take this type and pass it over to this optional area of the use form so this is I think this is called the generic correct me if I'm wrong an optional generic I'm not sure so I can send that over and now I am no longer able to say bananas here it will only allow register to be called with email password and confirm password which is quite quite nice furthermore I can say you know our submit will only send you this information and we can see that the type safety is assured typescript doesn't complain because even the handles submit coming from the use form API is uh now typesafe saying yeah I'm only going to send over email password and confirm password then nothing else which means if I export this sign up form values plural I can have type safety here as well so now I can say something like email and have the certainty that I have that field available so now I am able maybe to do some sort of mapping or additional uh work on this object without uh fear of losing some fields or using fields that don't exist and so on and so forth so now I can do the fetch I can do the Fetch and we know we have the slash API slash sign up I'm gonna await it and let's do http response here I'm not going to use any library because I don't want to complicate things even more so I'll just use plain fetch calls now we know this is gonna be a post because that's how we send encoded data encrypted that actually because it's probably going to go through https or something like that and we're going to send the body and the body will be a stringified what well we do not want the component password so there's no point to sending it we can just send the two email and the password like so now when we take the Json response we should log it and I think it's pretty much done that's that's all we need to do I don't think we need to complicate things anymore at least until we make the integration work between the back and the front end so let's see if it works um if I do fake user one and you say quality let's try something else I press enter and we got success true and we have 100 submit ready data so we did get Success Through from the server so let's click that and we see that we actually reached this point with success true meaning our API sign up end it up here after simulated one second delay and now things are starting to get a little bit more complicated uh you're probably already aware of this if statement you've seen it you've seen that we can also have a success false and I'm sending over an object specifying the field and the error that I'm imagining would come from an actually well-built back-end API that handles this kind of request and the question is how do we propagate and set errors that are sent from the server how do we reason about them in our form or in our container on top of the form and how do we get this message from the server onto the screen in the right field now we have a couple of options and from this point onward we we're gonna discuss things a little bit more because now we're actually implementing more specific features which are dependent on the architecture that we adopt and the practices that we have in the project and so on and so forth foreign oftentimes when I teach this to students I see people setting a state here set error State and then if this recent response success is not true they would set an error state so if this doesn't exist we might do something like Uber or something anyway and then they'll have a state here state use State and then they will pass the state down to the form like so and you said about errors and here they will Define the server errors and you'll have a specific API endpoint and then here onto the page uh because the Yahoo form handles the the errors not our simple tags they might use something from the react with form API or they might actually add all the way down here server errors so why I I've seen this approach couple of times with server errors about email or something now if we go down this path we end up with components that know about the server and we don't want our form to know anything about the server ideally it should know only about the things that it controls not about the outside environment so if your reflex is to set the arrows into the statement propagate them rule props I understand I kind of agree but I also kind of disagree and I will attempt to show you and convince you that there's a better way to do it and before we continue let's take a look at how we can set errors programmatically with uh react form so we have something called set field error or set error let's see what it does so set error takes a name and then an error object and then an optional flag to focus or not that field and let's take a look at how this might uh be so we can have something like a custom message so we can set an error for this input of type custom and send over a message so let's take a look at how this this would work I am going to go into our uh our form let's comment those two lines out and I'm just gonna attempt to set an error let's see like so and it's going to be on the email it's gonna be a custom message on the email so let's go and refresh the page and see what we get we don't get anything because I think Zod will kind of override whatever we're trying to set here but if we use a usif effect just to see how this works so on Mount if we have something like this I think we will be fine so we see the custom message so that's how the practice kind of starts with uh people that have this approach they see that they can set errors programmatically usually usually in use effects or as a consequence and so some sort of a synchronous action and then they do the thing that is super super hard to maintain in react they do the ping pong or the Domino between use State and news effect and that is that is the mother of all evils if we send this error state these server errors to the sign up form and then we pass it in here and then we start using this use effect pattern to react and to change the the data onto the page our components will grow in complexity to the point where it is a nightmare to understand and to maintain um their code bases if we go with this approach there will be a point where server error exists and the UI so the Dom shows no error why because let's see what happens the first time we encountered this error the first time we set this error state our main signup page or signup component updates it passes the props down to sign up form this form renders without any error because we're not using server errors in the render method anywhere and after it has rendered without anything we are using we're running this use effect so for a brief moment of time if you were to debug and maybe we have another use effect somewhere else and we take a look at what's available in the scope of this component we're going to see several errors and no errors displayed and this is very very confusing and we can do better so I'll get rid of this use effect and I will attempt to stay away from all the ping pong and the sets date and everything like that and I actually have a challenge for you so do you know if there is a way where you can stand or set an error or something do some sort of update in the child component without setting any state so the challenge is you are not allowed to set any state and I mean no State whatsoever no State Management no no nothing like that you're not allowed to use react State directly in your application but you are still supposed to make a change in the child do you think you know how you can do that leave a comment below if you actually knew before you saw my Approach so I'm just gonna I'm just gonna comment this out and I'm gonna add a console log here and I'll say uh which should uh set an error in the form here I'm gonna remove the server error and I will attempt to keep everything inside these handle submit function because this handle submit function could describe in a phrase or two exactly what happens with the submission of a login over sign up attempt so if we were to implement a story from uh you know a Sprint if you're using agile or something like that the text in the requirements of the story with almost match one to one with the code that we write in this method and that in my opinion is so much easier to maintain so how could we change the sign up form so how can we change the sign up form right here on this line without sending it new props without context because context also uses State and so on how do we do that there's actually a hook that people don't use too much it's a little bit tricky to use but it's super great and super strong in situations like this when the parent needs to change the child in a super specific way and we do not want to pollute the API props of the child we would like the child to just allow uh an unopinionated way for it to be changed and the parent to change the child whenever they see feet ideally we should also avoid rendering cycle so we should avoid setting a state in this pattern component because setting the state will run a lot of other use effects or reduce running other things in there and complicating the flow quite a lot foreign if you don't already know the hook that I'm talking about it's called use imperative handle so let's actually go ahead and uh look at it so we have use imperative handle it takes in a ref and creates a handle it also has dependency similar to use effect and so on feel free to skip this if you already know what use infinitive handle is and just see how we're we're running it you we're using it I'm gonna spend a few minutes describing what it does because a lot of people don't know it a lot of people are actually stuck with use effects and state beambongs as I like to call them so let's take a look at how this might uh work first of all we need to forward drafts if you want to use it so our form needs to accept the ref from the parent and then onto that graph it will inject if you will a couple of methods that are returned from the use imperative handle second parameter which is a function this ref will need to be type save we'll have to do some type safety for it and then add any method that is available here will be callable in the parent so basically here if we have a ref to the form so sign up form as a ref and we have sine F1 dot current that's number method if we want to have behavior in some method or full bar or how we want to call it so let's do something like sign up form ref current show errors let's let's say it's going to be that method if we want to show errors to exist on our sign up form and this would be sent here we would have to define a use imperative handle and inside this return map a return object provide the show errors method so I'm gonna start over with with this I'm going to copy this use imperative handle from the docs I'm going to place it here import it and for now I'm just gonna forward drafts like so without any additional parameters or type safety or anything like that close it now my eslint will complain of course I will add a display name [Music] or for our forward drive component and now we are called wood let's see what problem I have so oh okay so we have some errors on the signup page but that's not relevant what's relevant is that here we can have a set errors or how did we call it shower where you set errors and we'll see what will send as a parameter to show errors and I'm just gonna console log whatever it is here so I'll do something like that let's see if this actually works I am going to create a wrap another user a ref and for now I will not worry about pipe safety I just want to see the console log actually works and let's see if it does so if we get to this error State meaning if we submit the form with the error value this one right here so if we say Bob gmail.com if we do bob.gmail.com one two three four five six one two three four five six and we submit how we get a success true that's odd it shouldn't be successful are we really success true that's uh that's interesting what are we getting as a log let's see let's scroll all the way down oh apparently we're not sending the email and the password at all and that's actually an easy fix uh next this is specific to next.js if we make a fetch call and we send a body and we don't specify that we are sending Json now.js will not deserialize the body for us so if I do here content type application Json we should be good let's see if that works submit and still not working that's odd maybe it's something else not content type all right so the problem was the typo this should have been application uh that that was an interesting two minutes of My Life um anyhow now if I submit with Bob at gmail press submit I should get success false and as you see sign up form the code from sign up form here from the imperative handle has been triggered without setting Pros without editing States so right now this code has run imperatively hence the use imperative handle it did not allocate CPU cycles for updates in react which is actually a good thing in my opinion because it avoids so many additional side effects with use effects and state and so on now if we can get this set error and use it inside our imperative handle we're pretty much set because our backend already sends over the right type of information you also already sends over email as the key and a message about what's wrong with that particular object I also noticed that we are sending over two responses so I will do a return statement right here all right so let's see how we might get this that error to be linked to the content from the server nerves let's console log what we have right here so let's take a look at how Json Arrow Json response errors looks like I don't know what that was but anyways well I'm I'm gonna allow Json response to be unsafe From typescript perspective because we're probably going to cast it or do some sort of validation anyway in real applications between these two lines but yeah let's let's take a look and see what we get so if I press submit again we get email not available so we if we send this object with which is a record of type string and string we should be able to map it to actual errors on the page the same way we saw how we display a custom message for the the email field in the previous section of the video so as long as I send over these I should be fine let's take a look so set errors uh now should come here errors and this is going to be a record the type streaming string and we should be able to set errors and since I know these are uh key value pairs I can already do object maybe entries for uh errors then for each error like to set here or maybe even better let's take a look can we actually set multiple arrows at the same time now so we have set error and we do not have a set errors plural so we'll have to iterate yeah we'll have to iterate through them which is fine uh so I'm going to call set error from reactive form and this is going to be what this is going to be a a key and a error so I'll do something like that and that is pretty much it okay foreign so why is it complaining now I wonder oh it's complaining that key is of type string yet we only allow email password and confirmed password here so now let's just see if it works and then we'll take a look at type safety and make everything type safe including uh including this method right here the set errors and so forth so I'm gonna go ahead and refresh and pick again Bob one two three four five five submit okay six by the way okay submit successful error is email is not available it seems that we're not there yet let's see how this looks like because maybe I'm making a mistake here who knows submit again error email email not available and of course it was a typo so this should have been message and uh this was uh I think it was values or no it was entries and that's how it should be let's see if it works if I submit I get to see the error on the screen and if I make a change I am now no longer seeing that message so we've just propagated errors from the server directly to the input field [Music] we still need to make a little bit more efforts to make everything type safe and it's not that hard because all we do is we have this uh property this property this API method exposed so I can make another interface is going to be sign up uh API which contains this method and I think it's going to be like so so we Define the forward draft API first and then the props in the formal draft and now props is still sign up props refs however is sign up API and all these good there the only thing that we see as a complaint is the email password confirm password the keys that we're expecting here and I'm just gonna I'm just gonna cast them because this is not the typescript focus uh focused uh experience it is something related performs you just say something like that of course we could theoretically say that we do not accept any key and unless it is one of those but that just complicates things and in the end the object entries and object keys and so on will still need this type of casting for the key so might as well just keep it here and I think we can accept it as is for now I'm gonna make one more comment about this uh dependency array imperative handle the hook doesn't figure out that settler doesn't change during multiple renders so one thing that I like to do and again this is something that helps the with the mentability of code and uh it makes the orientations more clear for other team members and so on if you do not want to react to uh any of the changes for something that goes into the dependency array so the leader asks me to add in our case set error here if I know this function will not update ever or it will not update because of set error we can do another use web like so and then set it all wrap current is this and now instead of calling set error I will just set aerodrev dot current and now the leader no longer complains and my intent is very clear my intent is to use this API externally but I do not want to recreate it ever and that happens to be the case in uh in this situation uh at least all right now that that's out of the way we can export the sign up API and we can define it here we can say that we will use the sign up API this is going to be the type of the reference this is what our ref will have on itself when it is callable and if we go and pass in the sign up graph to our sign up or we see that the typescript system doesn't complain the only complaint is here where we actually need to say Hey you know sign up one might not exist foreign so now if we get an error instead of setting State and doing ping pongs and adding news effects and so on we call an imperative API exposed by the sign up form for parents of of the component hierarchy and inside the form we just have this one small thing here which will never remainder it's not related to the rendering cycle it is related to the programmatic API over power form so now if we refresh the page and we try it again it is going to work as intended now it needs to be Bob submit and we see the message the error message well we are almost done we are submitting the data we're getting server side errors uh we have validation I just realized right now that I have misspelled the sign so I'm just going to face that and keep rambling for a little bit mm-hmm there's only one thing left at least from the first demo that I showed uh in the first part of this video it's related to the submit button and how we might handle a mechanism for prevention of multiple submits at the same time has us covered in this situation I think inform state is uh another one another flag called is submitting and if we just check if it's submitting we can disable the button so our handle submit receives an unsubmit ready and if this is asynchronous so let's actually type it so so we're expecting a an asynchronous function if it's in a synchronous function each submit will be set to True while that executes and only when it finishes its submit will be toggled off now I'm gonna I'm gonna add a button that's already styled and has the condition for uh disabling basically I'll just do is submitting like hold up and then it's going to be submit again so let's see if the flag works and after the flag Works uh I will add so we have hold up and submit and it works and if we want to use uh Daisy UI we would use a button that looks like this and we will have a sending and a register label inside it so now we have this pretty register and we have a similar experience but now we can no longer click it and actually yeah so uh as soon as it becomes available again I have the the submission has uh finished we can click it one more time but we cannot click it while it's disabled and send it now finally we can take a look at how we might redirect and this is actually another topic that we can expand upon when it comes to the separation of concerns and how it isn't about splitting the code and that is redirections what happens when this form submits successfully who is responsible for the redirection and also usually sign up forms have another button called login and that button redirects the user to a login page and vice versa so let's let's briefly talk about redirection and then wrap things up hopefully you're still with me if you are by the way if you reach this point wow uh I just realized it took me like three hours or more to recall this so hopefully this was useful thank you for being here if you're actually still watching if you haven't liked and subscribed uh I don't know what you're still doing here if if you're not gonna come later and see my next videos so please subscribe please like and share this with uh with your friends where was I yeah so let's take a look at routing and how we handle navigation and uh that sort of concerns on successful registration we no longer need to show this uh form basically so what we can do is here when we successfully submitted we can use the redirection system provided by our application layer in my case with max.js it's going to be a use router from oh next router so I can take that out from on the import and use it on to the page and then here I can say router may be replace and let's go to the home page pretty simple pretty neat let's see if it works so I'll pick a user again I think I need more characters I submit and then I see the uh hello world sign up I didn't like the fact that for a brief moment of time I still saw the register already so one way to uh quickly solve that is to just add a small little timeout path at the end probably a little bit hacky but yeah I'll allow it I I don't mind it's gonna get cleared anyway when we leave the page so maybe have a second is enough to just make sure that it doesn't it never uh it never goes back to showing the register pattern as clickable so now when we go back oh we can go back because we use the replace so we need to type in the sign up URL again one two three four five six one two three four five six well register and there we go we are onto the home page that's all well and good but what about the login button what about that message and that button that you see oh already are you already user sign in how do we handle that in a way that respects the separation of concerns that we have so far so right now the sign up page kind of has too many concerns but it there's still kind of application related and then there's the form that has absolutely no application knowledge it just receives props so the login button exists in the sign up form so let's add it there however the URL and the ideas and the architectural constraints of the navigation system don't belong here for those of you that don't know with nexjs you need to have links like link href sign up so if we have a link coming from next link into the sign up page will not sign up page sign up form sorry we are not able to use our form anywhere else unless it's an xjs application that's a no-go in my book so how do we allow links like this one to make their behavior to to enable their behavior in our form without actually importing them and knowing about them in the form you might have guessed it it's probably with additional props but for now let's add it right here let's add the button let's add the log in here and see how it looks and let's see if this can be a secondary or what what options do we have so we have primary and second let's do secondary secondary and if I go back to sign up I see this log in if I click it it's still going to submit which is not that great if you ask me and it has absolutely no link so what's one thing that we can do well one thing is on click we can prevent defaults so uh let's do just that and see if we still trigger the validation so now we're no longer triggering the validation that's useful to know and what about the link well if we have this button onto the sign up form we kind of need to have the link there as well one option is to allow something called maybe a props suffix and allow the pattern to place whatever they want the pattern wants after this particular button so it's gonna be it's kind of going to be like a slot if you will from other Frameworks so we can do suffix and here I always forget what's the type I think it's react element or something like that let's see if this works I'm not entirely sure if it will import button and it magically Works wow if we go onto the page we still have the same behavior it's just that now the center form is not responsible for the login button it just allows the login button to exist there so there's nothing Stopping Us Now into this sign up page from using a link because links are a responsibility of the application layer and pages are an application layer entity so I think we can use a link just like that and if not uh we might we might be able to get around around this limitation let's just make sure we imported the right thing so no not the link from react and DZ why the link from next.js and now if I click it still nothing happens of course nothing happens because we're actually still using the sign up as the URL so we actually need to change it to log change it to login I think you can also go ahead and remove the event listener because the link itself will do that kind of prevention for us and if we create a page similar to index but for login we should be good to go log in and just log here like so and let's take a look so sign up goes to login there's the page click login we are on login we click back we click back we see the sign up page let's do a quick recap of what we have done today we have a form we have validation we have Cross Field validation we are disabling the field uh the submit button when we're submitting we have server-side errors integrated and we see how we can propagate individual Fields as well as we see how we can integrate application layer components inside our form so we saw the login form the login button and how that can link to a different form or page we've split the components the component the page into at least three components so we have a text field that we're using across the board we have a sign up form which only knows about the validation the local things the presentation layer and so on and we have the signup page that knows everything this is actually the last component that we could split but in interest of time and since it's not related to separation of concerns this video is more related to forms we're not going to do that but there's places that we can improve upon in this page in this component so we could extract the API into an API layer we can even split the page into two separate components one that only knows about the application the next JS application capabilities and one that knows about our API endpoints and so on but that is a topic for another time what is important is that our sign up form it is modular it only knows about the client side concerns of the form and it allows through extension for pattern components that are more knowledgeable to inject stuff that is not supposed to exist in the form like a link from the next JS Library oh wow uh we're actually done uh it actually took a lot longer than I expected I hope this was useful for you leave a comment and let me know what you thought about uh what we talked today uh if there's something that I could do to improve or that could make those videos better that's also welcome and I hope you subscribe and it uh you come back and check out the next video
Info
Channel: Vlad Nicula
Views: 12,313
Rating: undefined out of 5
Keywords:
Id: FXWD_etMJWA
Channel Id: undefined
Length: 95min 38sec (5738 seconds)
Published: Mon Jan 16 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.