THE ULTIMATE REACT-HOOK-FORM CHALLENGE - ALL THE FEATURES IN ONE FORM

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
good morning today i want to show how to build the most complex and feature complete form that i could come up with i called it the ultimate form challenge and it contains all the features that we as developers struggle with when working with forms so first of all as you can see it's multi-step it has three steps and each step has some peculiar features implemented in it so in this video we'll cover field validation and masking normalizing fields multi-step forms dependent fields for example when you want to show some field only if some other checkbox is checked using the form with material ui uploading files and uploading files using the drop zone package all of this will implement using react to form so press the like button share this video with your colleagues and friends and let's go so first step only validates the fields first of all it checks that the fields are not empty if i use the tab to jump between the fields it will highlight them and say that they are required fields i'll enter my name and surname now it also validates that the name doesn't contain numbers if i enter the number and then tap to another field it will show another error first name should not contain numbers and actually same for the last name last name should also should not contain numbers so we fix that and now we can go to the next step the second step contains the dependent field it only shows the phone if we check this checkbox here i need to enter an email and then maybe optionally a phone number and the phone number is masked or normalized in international phone number format so if i start entering some swedish phone number plus 46 one two three one two three one two as you can see it added spaces and formatted it properly now let's go to the next step and here i can drag and drop some files for example let's say i want to upload these four images i drag and drop them and i appear in this form now i can click next and here we see the results page from this page you can actually start over and see that all the field values are preserved first name last name email phone number and even the files and if you like what you see you can submit your form and see this success message if we open the project you'll see that i also have the uploads folder where all the data ends up being uploaded here we have the data json containing the form fields and i have all the four images i've tried to upload to the server now if you're already let's start building this project i'm going to begin by creating a new project using creator act up vx create react up the ultimate rhf form after the project is generated i'm going to go to this folder the ultimate hf form and install the dependencies yarn add and then we'll need the react hook form to handle the forms we'll need the reactor our dome because we'll need the multi-step and this will be implemented using the routes react router dome we'll need material ui stereo ui core material ui icons we'll also need the lid phone number js library to normalize their phone number lib phone number js react drop zone to upload the files up to validate the fields hook form resolvers to be able to use yup with react to form react confetti for the confetti in the final step and switzerland 2 for this nice success pop-up and then sweet alert too after the packages are installed i'm going to open the project open the terminal and i'm going to copy the server here you will have it in the code example attached to this video cp ultimate 4 completed server you will also need to install a bunch of dev dependencies to be able to use this server script and they will be included in the packages on of the attached project install the dev dependencies by running the yarn script and now i'm gonna add two scripts to run the app and the server concurrently first i'm gonna add start server it will just launch node server dot js and also the dev script it's gonna run the concurrently kill others to kill the previous running concurrently scripts 10 pm run start server and npm run start here you go now we can start working on our code first i'm going to remove the file set that we're not going to use anyway it's app.css apptest.js and log svg then we go to app.js and we remove the imports and the layout and instead we'll define the routing here import browser router as router route and switch from react router dom now inside of the layout we defined the router inside of it we use switch to only show one of the routes the one that matches that with current url and inside of it we define four routes route number one exact because it's the first route and it's the route route so we need to use the exact so it doesn't match with all the routes because all of them will contain the preceding slash the component will be step one don't worry about it yet we'll define the steps later now we copy it we don't need the exact with other routes the second one will be step two step three and result components will be step two step three and result now let's define the step components create new file step one dot js import react from react export const step one equals a component and for now we'll just return null duplicate this file create step two with the component step two step three and result now as you remember we have this the ultimate form challenge header in the top of all the pages let's define this component first create new component header.js and actually let's create a folder for the shared components that we'll use in this application new folder components move the header there and inside of the header component we import react we'll also use the typography input typography from material ui core typography we'll need to import the make styles factory function from material ui core styles now let's define the component export const header equals a functional component that returns a typography with the text that the ultimate form challenge component should be h1 we want it to be the first level header element and now we need to define the class names to do this we define the styles const use styles it will be the hook generated using the make styles function here we pass the object with field root and here we'll define the styles for our header margin here we pass the function that accepts theme in the arguments and we return an object this is where we want to wrap it into another round brackets and we need to define the field root with margin theme spacing three zero two those are just arbitrary numbers uh i've just tried different ones and it looked better font family we'll use the permanent marker it's the font from google phones and we'll add it to the project in a minute text align center font size 40 pixels color the pink text shadow 1 pixel one pixel dark magenta now we can get styles using the use styles hook and then we apply a class name to our typography styles root let's connect the permanent marker font to do this we'll need to go to index.css and in the beginning of this file add this import statement with the url that you can get from the google fonts when you select the font here we use the permanent marker font family now go back to upds rub the whole layout into a fragment add the header and also import the steps and the result pages step 1 from step 1 step 2 from step 2 step 3 from step 3 and result from result let's open the browser to see if it works correctly it looks nice so let's continue now i'm going to go to the first step and start implementing the form first we'll use the use form to get the register handle submit and errors we define the onsubmit function it's a function that will accept data for now we'll just navigate to the second step we'll need the history object we get it using the use history hook history equals use history and in on submit we call history push step two let's define the layout return for now div inside of it a header h2 step two and the form with two inputs input name first name type text placeholder first name and another one for the last name we pass register as a ref ref equals register and then we add a submit button in the bottom button type submit with text next all right we now have the form let's make it look nice to do this we need to define a bunch more components first let's define the container new file main container dot js import react from react export const main container here we'll need to import the container from the material ui import container from material ui or container and the make styles factory function make styles from material ui core styles now in the layout we return a container we need to have children and other props we pass the children through the container and pass all the other props to the container this way it will still be configurable component will be main max width excess now let's define the styles const use styles equals make styles we pass the function with a theme that will return an object with the field root with margin top theme space and four display flex flex direction column and align items centered now we get the styles in the component const styles equals use styles then we pass the class name styles root format the document and we are done go to step 1 and instead of the outer div use this main container next we need to define the form itself inside of the components folder create a new file form.js import react from react export const form equals a functional component it also needs to process the children separately from other props here we return a form that will get the props it will render the children we also pass the no validate properties and we also need to define the styles for it import make styles from material ui core styles cones use styles equals make styles theme root is an object with width 100 margin top theme spacing 1. now we get the styles equals use styles and then we pass it as a cluster class name equals styles root go to step one and now use the form component that we've just created we're almost done let's define the input fields create a new component input dot js import react from react export points to input inputs will need to handle the ref so we use forward ref from react so we need to import it as well and now we'll get two arguments first one is props and second one is real note that i'm not wrapping them into the curly brackets now because it's two separate arguments i'm going to return a text field with variant outlined margin normal input ref graph that we get from the graph that we get through the arguments because we wrapped our component into the forward draft function full width and all the props that we get through the arguments format the document and now let's go back to the step 1 and remake our inputs we're going to use the input component that we've just created now don't forget to import it input from components input now we'll pass the label instead of placeholder and same for the second argument for the second component it should be last name for now we're done with the inputs so let's style the button create a new file call it primary button dot js import react from react export const primary button is a component will accept the children and other props we'll need to imprint the button from material ui or button we'll import make styles from material ui core styles now we'll define the styles const use styles equals make styles with theme and here we define the root don't forget to wrap everything into round brackets so that will return the object here with the field root and margin theme spacing 302 now inside of our component we get the styles using the use styles and then return a button inside of it will render children and we'll pass the following props to it type submit full width variant contained color primary class name styles root form of the document and the last thing we'll pass is all the rest of the props go back to step one use our button it's called primary button we need to import it from the components primary button let's look at the form we're almost there we need to style the step heading and implement the form validation to start the heading we're going to use the typography remove the h2 make the typography be the h2 component and variant h5 it should look like h5 but actually be the h2 header let's call it step one and add the unicorn emoji make sure that you import the typography from material ui core typography and now time to implement the form validation we already have the errors that we get from the form and to validate the inputs we'll use you import asterisk as u from u and let's define a schema const schema equals u object shape and here we define the shape of our schema so we have two fields first name which is u string that matches with the rig x here we define some something that does not include the numerics to do this we use the following syntax beginning and then and in the middle we have the group with not from zero to nine and if it matches with this regex then the error will be first name should not contain numbers and it should also be the required field first name is a required field actually same for the last name last name is a string if it matches with something containing the numeric it will say last name should not contain numbers and last name is a required field now we need to use this schema to feed it to the use form hook to do this we import the u resolver from hook form resolvers now in our use form we pass an options object with resolver that equals u resolver with schema now we also define the mode it will be on blur which means that the validation will trigger when the inputs get unfocused or blurred now we go back to our layout and here we'll need to pass a new probe called error which is a boolean so we cast errors and then our field name in this case first name to boolean using this double exclamation mark and then we need to pass the helper text helper text errors if it exists then the first name also exists then we pass the message here i use the optional chaining operator so if any of the objects like errors or first name is undefined then the whole construction will return undefined instead of throwing the undefined doesn't have the field first name or the field message error form of the document and then we repeat the same for the last name input but we change the fields last name and here also last name let's make sure it works so we try to enter one and then tab okay we get the error remove the one and we get their own last name okay good i will try to enter my name maxim even off and as you can see something is wrong let's check our regex and i forgot the asterisk in the end because we need to match all the other letters let's go back try to input my name and everything works it means that we're done with the first step but before we move to the next we need to think how will we store the data between the steps and to do this you will need to use either a context or something like redux for mobx to store the global state between the wizard form steps let's go back to the code and define a new component we'll call it data context.js we need to import react of course create context use state and use context from react now first we define the context data context equals create context we'll export the data provider expert const data provider that is a component that accepts children and returns data context provider it renders children and we need to pass the value to it we'll store the intermediate data in this context you could use use reducer here but i'm gonna use a more simple solution that will work for us here we'll just have the state data set data and we'll use the use state with default value of empty object i'm also going to provide the function set values a function will accept the values as an argument and then inside of it we'll call set data previous data and we calculate the new value by spreading the previous value and merging it with the new values the benefit of this simple method of storing any kind of data in this universal provider is that it's very simple and is very easy to implement the downside is that every time you change any of the fields the whole object is getting regenerated so all the components that are subscribed to the data changes will be updated and re-rendered it's not a concern for a small application but you should think about it if you're going to use it in in somewhat bigger code base so if you're writing a bigger project then i recommend to use something like redux and you will find an example of how to use react to form to build wizard or multi-step forms with redux in the video description now we need to pass the values to the provider value equals data and set values format the document and now we'll create a custom hook called use data to get access to the data easier expert const use data equals a function that is actually just a wrapper around use context where we pass data context to it now we need to go to the index.js and wrap our application into data provider import data provider from data context now we can go back to step one get the data and set values function from the use data hook const set values data equals use data we'll need to import use data from the data context and now we can do two things first of all we can provide the default values for our form we do it by passing the default values option where first name is data first name and last name is data last name also on submit we can call the set values with the values from the form now we pass the onsubmit handler it will be handle submit that we get from the use form that we'll use to wrap the onsubmit handler that we've defined here now we can go back to the browser try to enter the valid values and press next and we are now on the step number two back to the code so for the step number two we'll handle an email and an optional phone number that the input that will be triggered with the checkbox if the person has a phone so we'll start with the layout we'll render the main container inside of it we'll render a typography for the header with a unicorn sign and step 2 then we'll render a form with an input with type email label email name email and it's required let's define the hook form const register handle submit watch we'll need it later and errors from use form we'll also need the history object const history equals use history now and we'll need the data cost data set values equals use data now when we use the form we can already pass the default values it will be an object with email data email has phone this is our checkbox value it will be data as phone and phone number from data phone number mode just like previous time will be on blur we will now watch the has phone value using the watch method that we got from the use form watch has phone we'll pass the register as a ref to our input rep register now let's define the checkbox that will enable the phone field we'll render it using the form control label with control being a checkbox and label being do you have a phone and we need to pass a bunch of arguments to the checkbox first we pass the default value it will be data as phone then we pass default checked it will be data as phone as well color primary and input ref register we also need to pass the name which is has phone this checkbox will update the value of the hash phone field in the form and now we can add the conditional rendering of the second input as phone and input withdraft equals register id phone number type tell labeled phone number and name phone number format the document and now let's go and define the you schema import asterisk as you from you const schema equals you object shape and we pass the object there we'll validate only the email here email you stream email if it's not an email then email should have correct format will be the error message and it's also required and it will say email is a required field now we need to import the upresolver import resolver from hook form resolvers now we pass the resolver to our hook form with u resolver with the schema we can define the onsubmit function const on submit and it will be the same as in previous step it receives the data and inside of this function we're gonna call history push we need to go to step three and also we set values from that form let's make sure that we pass the onsubmit handler it should be handle submit that we use to wrap our own submit function let's go to the browser let's check if we can enter an email let's see if we can toggle the phone number seems like everything works let's add the error handling to our email input error which is a boolean flag should be exclamation mark exclamation mark errors email and helper text should be errors email message let's check if it will show us an error if we enter invalid email it shows it and we can continue let's add a submit button primary button with text next now we're going to normalize or mask the phone number so it's formatted correctly in international phone number format to do this we add an onchange handler to our phone input it will be a function that will get an event and inside of it it will set the event target value to be the result of the normalize phone number function where we pass the event target value so we'll process the event target value change it format it properly and then it pass it back to that target value we need to define the normalized phone number function now scroll up and define a new function const normalize phone number it's a function that gets the value and inside of it we get the new phone number using the parse phone number from stream and we pass value to it we get this parse phone number from stream from the lib phone number js parse phone number from stream from lib phone number js now if we don't have the phone number so the parse phone number from string could not parse it as a phone number if no phone number then we just return the value just keep it unchanged otherwise we return phone number format international let's go to the browser try to enter the phone number class 46 for example one two three one two three one okay and it's formatted we can enter the email valid email and then press next we are on the step 3 and here we'll need to handle the file uploads using the drop zone let's go to step 3 and start filling in the layout here we'll again use the main container we'll pass a typography to it actually i'm going to copy it from the step 2. go to step 3 pass the typography change the title to step 3. don't forget to import the main container from components main container and import the typography from material ui core typography now we'll define the form itself and we'll import it from components formed inside of it we'll have the file input we'll define it a bit later name files control control what is this control pro just like register you can also get the control property from use form and you use it when you use controlled inputs for now let's just get it from the use form and then i will show you how we'll use this control property that we pass to the file input that will create in a minute const control handle equals use form we'll also need history equals use history let's import use form from react hook form and let's import history from react router dom we'll also need the data from the context const data set values equals use data now we can use this data to pass the default values from the form it works with the files as well so we pass the default values with files data files let's also define the handle submit const on submit equals a function that receives data and internally it calls history push result and also sets values to data we pass it to on submit prop of our form wrapped into handle submit and we'll also use the primary button with text next i forgot to add the component h2 to the typography and variant h5 we'll also need to fix the use history import and also import the primary button from components primary button now let's deal with this control from the use form you use this property with the control inputs where you need to manually handle their own change events on blur on focus etc so let's define the file input go to components new file file input.js here we'll deal with the drop zone import react from react export const file input equals control name that's the props that we need here and then we return the layout first of all we'll use the controller from react hook form so let's import it import controller from react hook form that's a special component that allows you to wrap the input that you need to control manually if you cannot just pass the ref to it usually it happens with components with custom components like drop zone or some material ui components or some other custom components that don't provide the ref api we'll need to pass the control to it through the control probe we also pass the name from the name property default value will be an empty array we don't need the closing controller and we need to pass the render instead render is a function so controller is actually an implementation of the render props pattern so we need to pass a function there that will accept on change on blur and values and and the value and we return a fragment inside of which we're we're under the drop zone there are two ways to use drop zone you can use the hook or you can use the component in my case it's more compact and easier to implement using the drop zone component we'll need to pass the on drop handler so we know when we actually drop the files into the drop zone and we actually just pass the on change from the controller so when the drop zone will accept the files it will trigger the on drop function the callback and it will trigger the on change on the controller so reactbookform will know that the file input was updated on dropbookpath we passed the onchange the dropzone is also a renderprops implementation so we need to pass the function there that will render our drop zone component the visual representation of drop zone component and this render prop function receives two functions get root props and get input props so here we'll render the paper from the material ui variant outlined and then we pass all the root props get root props inside of this paper we will render a cloud upload it's an icon we need to import it from the material ui icons import cloud upload from material ui icons cloud upload we also insert 10 input we pass the get input props to it get input props and also pass the name because it needs to match the name on the controller and the on blur handler that we get from the controller so we know or react to form should know when we unfocus the input on blur equals on blur and we also need to render the message for the user because the input will be invisible so render a paragraph saying drag and drop files here or click to select files we'll also drop zone and this is why we are wrapping everything into a fragment we render the list from react to form inside of which we will map through the value which in our case is a list of files that we drop to our drop zone so we do value map we pass a function to it f will be our file and index will be the index we render the list item which we also need to import we're under the list item icon and we render the insert drive file that we import from material ui icons insert drive file below the file icon will render the text inside of the list item text primary will be the file name and secondary will be the file size we're almost done we can format the document and now we can provide the styles for this component we're going to define the use styles equals make styles we pass an object there with root element and with an icon styles for the icon so the root element for the wrapper of our drop zone the paper should have background color i think that ee will look nice like the light gray text align should be center cursor should be pointer text color should be 333 a darker kind of gray padding should be 10 pixels and margin top should be 20 pixels the icon should have margin top 16 pixels color 888 and font size it will affect the icon size actually 42 pixels make sure that you import the make styles from material ui core styles then inside of the file input component we get the styles const styles equals use styles and then we pass the styles to the paper plus name equals styles root and to the item class name styles icon make sure to provide the key to the list items we cannot guarantee that the files won't have the same name so we pass the index here which is suboptimal but well it needs to be unique now we can go to step 3 and import our file input from components file input now we open the browser click on this drop zone area pick some file for example one of the thumbnails of my previous videos as any and then you can see that it appeared in the files list cool that means that we can go to the last step we're now on the results page go back to the code open the result and let's start with the layout so just like on previous pages we returned the main container that to wrap around the typography we pass the component that should be h2 the variant h5 we specify the form values header now below that we'll render the table of all the form values table container component paper we need a closing tag inside of it we're under that table inside of it we're under the table head and inside of it we're under the table row and inside of it we're under two table cells table cell number one it will say field and table cell number two value table sale cell number two should have a line right now we render that table body below the table head table body inside of it we're under the mapping function that will do through the entries and we'll get the entries from the data so let's define the data const data equals use data to get the entries and we only need the form fields not the files we'll do the following const entries equals object entries from data with filter only the entries were the entry key which is the first item in the entries array entries are basically arrays of two items first one is the key of the field and second one is the value is not equal files so filter everything but regular fields and it's good to also save the files files equals and data now we can go to table body and map through the entries entries map where we get an entry and then we return a table row inside of which we render a table cell with text of the entry key which is entry 0 and another table cell with the entry one the value that we will cast to stream here we'll need to have another round bracket and we will use the entry key as the key for our table rows entry zero this is how we render the form fields we can format the document and now below the first table container if we have files then we render a fragment with another typography component h2 variant h5 we're under the files title here and then we also render a list there we'll map through the files files map we pass a callback function that will get the current file and its index and then internally it will render the list item key that will equal index and inside of it will render the list item icon with the insert drive file let's import the insert drive file icon import insert drive file from material ui icons insert drive file and then below the icon we render the list item text with primary text that equals f name and secondary that equals f size just like in the file input and then below that we're under the primary button with text submit and with an on click handler that is on submit we'll define it in a second and now we also render a link from react router dom that says start over and actually leads us to the root route now we have all the layout let's define the on submit this function should actually post the data to our server so first of all this function should be async because we are going to use the fetch method here and it's based on promises and it's easier and better to use the sync syntax there because it's easier to read we'll need to define the form data because we will need to send the files along with the regular form values and to do this you need to send them multi-part form data and it's easier to do using the form data object const form data equals new form data if we have files if data files then we call data files for each and for each file we append the file to the files field file file name passing the file name is optional you don't really need to do this and then we map through the entries for each entry we do the same we call form data append entry zero so the form field will be the name of the key and form field value will be entry one so the value of the entry then after we got the form data filled with the values we can get the response object calling the away fetch http localhost 4000 the server is listening on the root route we pass the options object method should be post and body is form data as we are posting the form data the fetch method will actually determine that the header should be multi-part form data and then after we got the response we check if response status is 200 if we didn't get an error then everything went smoothly we can use the sweet alerts that we need to import import swell from swede alert too and we're gonna call the swal fire great job youth past the challenge and success icon and also we'll need to show the confetti and it's using the confetti components from the react confetti so we define the state const success set success equals use state and by default is false then after we fire the sweet alert we call set success true now if success is true then we render confetti let's format the document and now we go to the browser and we'll try to submit the empty values submit and we got response to hundred good one last thing to do is to add some nicer styles to it so let's define the use styles hook equals make styles we'll need to import them import make styles from material ui or styles inside of this object we'll define the root style that will be margin bottom 30 pixels and that table margin bottom also 30 pixels now we can get the styles styles equals use styles scroll down to table container plus name equals styles root and the table has class name styles table let's go to the browser and try all together now name maxim surname even off next email test test.com do i have phone number yes plus 46 one two three one two three okay it's formatted next let's select the drag and drop select the file actually we can select few files let's select three of my previous thumbnails open next we have we have all this in the results page press submit and we're done now if you go to the code you'll see that we have an uploads folder with the data that we send to the server and that thumbnails that we've sent along with it thank you for watching let me know in the comments if i didn't cover any of the features that you struggle when working with forms or if you have any questions regarding this video subscribe to the channel and join my discrete server to pick the topics for the next videos see you next time
Info
Channel: Maksim Ivanov
Views: 33,434
Rating: 4.9346156 out of 5
Keywords: react, reactjs, web development, react-hook-form, react-hook-form multi step, react-hook-form wizard, yup, react hook form yup, react hook form material ui, react-hook-form dependent fields, react-hook-form mask
Id: U-iz8b4RExA
Channel Id: undefined
Length: 45min 39sec (2739 seconds)
Published: Wed Aug 19 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.