‫So probably where React hook form
‫shines the most is in form error validation.
‫And so let's now learn how to use those features
‫to our advantage and do a few more interesting things,
‫for example, creating a reusable form row.
‫Now, before we do any of this,
‫I first want to do a small fix here.
‫So basically I want the entire layout here
‫to stay fixed except for this part here.
‫So whenever I scroll, then only this main part here
‫should actually scroll while the rest of the layout
‫should stay in place.
‫So here in app layout, all we need to do
‫is here in this main part,
‫set the overflow property to scroll.
‫And so now if we go back here,
‫yeah, nice, that's fixed then.
‫Now let's move to form validation.
‫So as I hinted at earlier, we can add some more stuff
‫here to this register function.
‫And in particular, we can as a second argument
‫pass in an object for validation.
‫So here we can do many, many things,
‫but the simplest one is simply
‫to define the required property
‫and then here we can actually set an error message.
‫So, for example, "This field is required".
‫And let's grab this and paste this into all of our fields
‫because all of them are actually required.
‫So then here we get some problem.
‫Okay, and here another one and one more
‫and also here for the description.
‫And again, we will take care of the image here later.
‫So with this, we made all of our fields required
‫and so let's see what happens
‫if validation fails for one of them.
‫So this handlesubmit function here
‫is actually called each time
‫that we attempt to submit the form
‫and at that point all our validations will be executed.
‫So all of this here will then be checked.
‫And in case that there is one error
‫in one of the validations,
‫then handlesubmit will not call
‫this onSubmit function here,
‫but instead it'll call the second function
‫that we pass in here.
‫So let's call it onError.
‫So then onError here again and this one
‫instead of the data receives the actual errors.
‫And so let's lock those to the console.
‫All right, let's just reload here.
‫And then here I will add some name, some capacity,
‫but the rest I will leave empty.
‫And so as I attempt to submit the form,
‫then here we do actually get these errors,
‫so from this line 69, which is exactly this.
‫So again, the reason is that validation failed
‫for this field and for this one.
‫And so then not this function was called,
‫but instead the second one that we pass in.
‫So here we can exactly see what errors we have.
‫So again, in the description
‫and the regularPrice field, great.
‫So that's the most basic validation,
‫but we can go a bit further.
‫So here for example, we can also set a minimum value,
‫or we could also set a maximum value,
‫but in this case, let's just go with the minimum.
‫And then here we need to specify the value,
‫so the minimum should be 1
‫and then we can also specify the message
‫in case data validation fails.
‫So capacity should be at least 1.
‫And so now if I write 0 here,
‫then even though I filled this field in,
‫it will still fail validation.
‫So indeed now here it is,
‫or actually right here in maxCapacity.
‫Okay, and then let's do the exact same one here
‫for the price and then let's also do something here
‫with the discount.
‫So the discount might actually be 0
‫which is even the default value,
‫however, there is something really important,
‫which is that the discount needs to be less
‫than the regular price.
‫So we cannot have a price of 100, but a discount of 200,
‫that's just not possible, right?
‫And so for that, so for that kind of situation,
‫we can write our own custom validation functions.
‫So for that we specify validate
‫and then here we write a callback function.
‫And this callback gets as an argument the current value
‫that is currently being input in the field.
‫And then here we can write any kind of logic that we want
‫and as soon as that logic returns true,
‫then the field will be correctly validated.
‫So just to test this, let's just check
‫that this value should be greater than 100.
‫So just, again, to test here, to write something here,
‫then let's remove this one
‫so that we don't accidentally submit the form.
‫And so let's see, and actually the discount is not here,
‫so there was no error.
‫So because right now it is greater than 100,
‫but if we try 50, then here we get for the max capacity
‫or actually for the discount that there is an error.
‫There just isn't a message because we didn't return one.
‫So we can also return a message here.
‫So basically if it is not true,
‫then we will return this second part.
‫And so then whatever we return from the validate function
‫will become the error message.
‫So let's say "Discount should be less
‫than the regular price", okay.
‫So of course we don't want to compare the value with 100,
‫but really with the actual price, so with this one.
‫So how do we get that value?
‫Well, there's a nice function that comes here
‫also from use form, which is called getValues.
‫And so here we can call that function
‫which as the name says gets all the values from the field,
‫or from the entire form.
‫And so then after calling that, this returns an object
‫and then from there we can get the regular price,
‫so regularPrice.
‫And here this regular price should of course
‫be actually greater or equal than the value
‫or in other words, the value should be less
‫than the regular price,
‫only in this case this will be true
‫and the validation will go through,
‫but otherwise, then we get this problem right here.
‫So let's check that.
‫So 200 and then here, let's say 500.
‫And indeed in the discount, we get that the discount
‫should be less than the regular price.
‫All right, so this was a nice use case
‫for writing our own validate function,
‫but this can really be helpful in all kinds of situations.
‫Okay, but now how do we actually get
‫these kind of error message from our console.log
‫right here into our application?
‫So basically we will want to display something here
‫or here on the site telling the user
‫that something is wrong.
‫Well, we can get these errors once again
‫from the use form hook by using formState
‫and then from that object we can read the errors property.
‫So errors, taking that from formState.
‫So let's try to log that to the console
‫and there it is.
‫So now we have the same log twice, once from line 71
‫which is the onError function and then from 52
‫and so that means that these errors
‫are exactly the ones that we saw before.
‫All right, and so now here we can use
‫that error style component that we already have
‫and display that here so in case data is an error.
‫So let's use optional chaining for that.
‫So .name because that's the name that we registered here
‫and then let's grab the message from there, so .message.
‫And if that exists, then we want to use
‫that error component that I just mentioned.
‫So here it's done, again, errors.name.message.
‫All right, nice.
‫And so now if we try to submit this,
‫well, of course, it only shows here
‫because we only implemented it in this one.
‫Later then all of these fields will get a message like this.
‫But before we go any further,
‫I think it's a good idea to now actually abstract
‫some of the things that we have here into its own component.
‫So what we're doing here
‫is actually quite repetitive all the time.
‫So we always have this label, then always this input
‫and then we will always also have this part.
‫And so I think we can abstract all this
‫into its own FormRow component.
‫So let's create that here.
‫So FormRow and then let's just grab everything we have here.
‫And now we have a bunch of problems, so let's fix them.
‫So we need to grab all of this code here obviously too
‫and so this will now create a huge mess
‫because the form will completely stop working,
‫but, well, that's just how refactoring works.
‫Now here we are actually importing it from the wrong place.
‫Let's undo that.
‫So we want it from styled components.
‫And now here we have the problem that this already exists
‫and so here we need to again use that trick
‫where we call this the StyledFormRow just like this.
‫So what do we actually need to make this work?
‫Well, probably we should pass in this label right here,
‫then also the error message
‫so that we can use it down there.
‫And then actually input is the only one
‫that I want to pass directly into the component.
‫So the input should still stay in the form
‫because, of course, we don't want to have to pass
‫all this kind of stuff here into this component.
‫So let's also accept the children prop,
‫remove that from here, and instead use the children.
‫Okay, so here this needs to be dynamic,
‫so this is what is going to be the label
‫and then here it will be the error.
‫So if there is an error, then just place that here.
‫And the same actually for the label.
‫So let's also already account
‫for a situation that there is no label.
‫So that might be, and now the only thing
‫that we still need to make dynamic is this part right here.
‫So this name is basically always the ID of the input.
‫So the way that this works
‫is that when we then click the label,
‫the input will be selected.
‫So these two are basically connected by this ID.
‫So the way we can access that ID there
‫is by using a nice trick that we have never talked about
‫and that we never used, but now let's actually use it.
‫So this input here is what will be the children, right?
‫So this right here.
‫And since we know that we only have one children,
‫we can actually do this.
‫So, again, this children will be the input
‫and that input here always receives an ID, so as a prop.
‫And so we can do children.props.id,
‫well not here, but right here.
‫So children.props.id, beautiful.
‫And so this component now work and let's maybe, yeah,
‫maybe we shouldn't have cut this
‫because then we cannot really test this.
‫So let's just put it back.
‫Here it needs to be again styled only form
‫and so now it works just like before.
‫And so just to test, let's then include our new FormRow
‫which will then again create a conflict,
‫so let's rename it everywhere.
‫And you don't have to do this,
‫this is just so that I can show it to you here in the code.
‫So FormRow2 and so why it's not working now,
‫I mean, what's the problem?
‫And probably it's just because of this.
‫Yeah, there we go.
‫So now let's attempt to import
‫that FormRow that we just created.
‫And so this then receives as an input the Cabin name.
‫The error is this one right here
‫and then simply this input here as a child.
‫So this is a lot cleaner, but here, of course,
‫it's not a string, but more like this.
‫And so this works now exactly the same as before.
‫And so now we are ready to converse
‫all of this here through these other form rows.
‫So let's change this.
‫And here I will just copy this part.
‫So here that's Maximum capacity
‫and here it's then errors.maxCapacity
‫and here we can get rid of that label.
‫And since we're here, let's get rid of this old one as well.
‫All right.
‫Let's place something similar here
‫just so then I can easier copy-paste the rest.
‫So, well here there's nothing to copy-paste yet,
‫but anyway, so here this one is already done.
‫then here we want the Regular price
‫and the errors are of course called regularPrice.
‫So this kind of refactoring here
‫is of course always a little bit of work,
‫but, well, I also didn't want to write it
‫immediately like this because then it might have been
‫a lot more confusing for you.
‫Okay, so description for a website.
‫And then, well, here for now, let's just specify the label
‫which is Cabin photo and there we go.
‫So I think that, yeah, it looks like we have
‫this completely converted now.
‫And so if I now try to add a cabin,
‫then here we have indeed a problem in all of the fields.
‫Let's try to set this one to 200 and also notice
‫that immediately after we started typing here again,
‫the error disappeared.
‫Now this one also should have disappeared.
‫Let's just try something else.
‫And so now indeed the error is gone.
‫But if you make it 1000 and try that again,
‫well, now for some reason that error is not error,
‫so maybe we didn't give it the right name here.
‫Actually it looks correct.
‫And so maybe it's just not appearing
‫or, well, maybe the other one is incorrect,
‫so the regular price.
‫But nope, everything correct and the same here
‫and the same here.
‫Well, this is not super important either,
‫but let's just try this again.
‫So let's check and so now actually
‫we do get the error again.
‫Now it's gone and now it's back, great.
‫So all we had to do was to actually reload the page here.
‫Now, this function here is actually
‫not really that useful, right?
‫So logging something to the console
‫is not really that important.
‫And this is the only thing that we need
‫this function for right now
‫BEcause the errors are simply coming right here
‫from the formState, right?
‫And so, yeah, this isn't doing anything,
‫but I will just leave it here for you
‫so that you know it exists.
‫So you might, for example,
‫also lock something here to Century
‫or to some other error monitoring services like that.
‫And yeah, so just to see have the form actually still works,
‫let's now submit it again.
‫So add the cabin and beautiful, there it is.
‫Now just one final thing is that we might want to disable
‫all of this here whenever the form
‫is actually being uploaded, so while isCreating is true,
‫so just as a small final step here.
‫And just one more and also here.
‫so let's try that again, 004, 10 people, 250, 25.
‫Yeah, you saw that, it turned gray there
‫and then here it is, great.
‫And so our form is almost working.
‫All that's left to do is to take care of this cabin photo.
‫And so this is where we are going to upload a photo
‫to our Supabase bucket.