Uncontrolled Inputs Overview0:00
We've seen how controlled inputs work in React. Let's now contrast this with an uncontrolled input and we are going to work with a little form. The form in question is at the bottom of our app, this new puppy form. So it's got two fields, a name and personalityTrait and you can see there's also a file input. But I've grayed this out and it's disabled because we sort of need a backend to handle this and we'll take care of this later in the course.
because we sort of need a backend to handle this and we'll take care of this later in the course. Remember we had broken down a new PuppyForm component and this is just a normal form. There is no specific action or event listener at this point. So if I try to fill this form, let's say T-Rex, which is my dog, uh, very barky and then I hit submit, it looks like it reloaded the page. We're back at the top. And if I look at the URL bar, you can see that it's added the form field values
Prevent Default Submit0:51
We're back at the top. And if I look at the URL bar, you can see that it's added the form field values as query parameters and that's the default browser behavior for GET requests. We don't want that here. We are building a pretty cool slick reactive single page application. And so we don't want to reload the page, go back to the top and have form field values in the URL. So let's take some measures to prevent this from happening. In my form, I will add an event listener for submit.
So let's take some measures to prevent this from happening. In my form, I will add an event listener for submit. And so we indeed going to get the event and I'm sure you've all done this at some point to prevent the form from submitting and refreshing the page. We are going to prevent that default with event.preventDefault(). So now if I get rid of the query parameters and I scroll down to submit the form again, this time we'll go dusty, very fluffy.
and I scroll down to submit the form again, this time we'll go dusty, very fluffy. Uh, when I submit you can see that this time I haven't scrolled back up and the URL hasn't changed. So we preventing the default behavior from the browser but nothing happens because we haven't set anything in our code. So how are we going to keep track of our two field values and do something with them and submit?
So how are we going to keep track of our two field values and do something with them and submit? In the previous lesson we've looked at controlled input and that could work here as well. You could have const, name = useState, and then trait = useState, and then our first input the name input would have a value of name and an onchange of setName(e.target.value). Yep. And then we would have the same for the personalityTrait, value = trait.
Yep. And then we would have the same for the personality trait, value trait and unchange set trait. So we're not going to do this, but if we were to do this we could here console.log the name and the trait. So Trex loves fetching and when I click addPuppy you can see that we've logged our two state values. So that's definitely a valid way to handle forms in React. And matter of fact, this has been the way for a long time,
So that's definitely a valid way to handle forms in React. And matter of fact, this has been the way for a long time, but like I said in this lesson, we're going to take the uncontrolled input approach. So we need to find a way to pass this form data to our submit event. Alright, so let's undo everything we've done. I'm going to undo the control inputs and the useState declarations. So we're back to where we were.
Using FormData API2:57
and the use state declarations. So we're back to where we were. And so we're actually going to use web and dumb APIs. This is not react specific, but whenever you submit a form you're able to obtain what's called a formData interface. And this is going to allow you to get the values for the fields. You can see formData that gets, so let's create our formData.
You can see formData that gets, so let's create our formData with const formData = new FormData(). And we're going to pass the event.target. We're not going to worry about the TypeScript warnings because we're going to backpedal once again, but let's try to get the name with const name = formData.get('name') and const trade = formData.get('trade'). And then we're going to log both of these. I'll wrap them in an object.
And then we're going to log both of these. I'll wrap them in an object. So we have the key value pairs in the console and let's clear this and we're going to try again. Flex loves running and I'm going to add the puppy. And you can see once again we have the two field values, but this time we're using the formData interface instead of relying on react states with a control input. As you can imagine, if you have lots of field that gets super annoying real quick.
As you can imagine, if you have lots of field that gets super annoying real quick. And one cool way to get all the form data values in one go is to do object that from entries and pass the form data. So this should also give us an object with all the field values. So name and trait clear up and I just realized I wrote felt so we'll fix this to flex for this one resubmit and yep, here's our object with all the form fields.
React 19 Form Actions4:19
for this one resubmit and yep, here's our object with all the form fields. So this works and it wasn't too hard to implement, but since React 19 we can leverage the new form actions that are going to make things much simpler for us. Check this out. I'm going to comment out the submit. We don't want it anymore but I'm going to keep it here just to compare with what we're doing now. And instead this time I will pass an action to our form element And this can be once again an inline
And instead this time I will pass an action to our form element and this can be once again an inline function and let's keep it completely empty for now and go and resubmit the form T-Rex loves food submit. Hmm, interesting. The behavior was different here. We haven't reloaded the page, we haven't passed the data fields in the URL bar and the form has been reset. We didn't have to capture the event and then prevent the default behavior.
We didn't have to capture the event and then prevent the default behavior. It feels like React kind of took over for us. Now what if I told you that instead of having to construct our form data, I could directly here receive it and let's type it as a FormData interface and inside our block here I'll move the console.log of the object that from entries and uncomment it and see what happens. So we are not preventing the default behavior,
and uncomment it and see what happens. So we are not preventing the default behavior, we are not constructing our new FormData instance. We literally receive it and log it. Let's see what happens. Dusty loves snow submit, hey the exact same behavior than before but we didn't have any work to do. React has taken care of preventing the form submission, it has obtained the form data instance and it also has reset the form for us.
it has obtained the form data instance and it also has reset the form for us and behind the scenes it has done a few other things as well. Alright, so let's make this actually useful and instead of just logging the data in the console, we are going to submit our new puppy and add it to the list of puppies. In order to make this happen, we need to make some changes. Right now our puppies data is just static data.
Convert Puppies to State6:04
In order to make this happen, we need to make some changes. Right now our puppies data is just static data that we get from a file and then pass down to the components. If we want to be able to update that array of puppies, we need to turn it into state. So we need to somewhere set this data in a state object and then update this with the state setter. So let's visit our app.tsx file, our main components that is the root component for the app.
So let's visit our app that TSX file, our main Component that is the root Component for the app. And remember that we have this puppies data that we then pass to the PuppiesList as well as the ShortList. But this is just a static array of six objects. What we want to do instead is turn it into state just like we have state for the likedPuppies and the searchQuery. And once again I believe this state should live in the main Component because this is where it's going to be shared.
And once again I believe this state should live in the main component because this is where it's going to be shared with the PuppyForm, the PuppiesList and the ShortList. So check this out, we are going to create yet another piece of state consumedPuppies and setPuppies. And look what happens here. This is important. We use useState and define it to an array of puppies. And the default value instead of just being an empty array is the actual puppies data. In other words, we're going to set the default values
of just being an empty array is the actual puppies data. In other words, we're going to set the default values for a puppy state to be the data that we're importing from that file. Now we're going to have a problem and a naming collision because puppies here is already occupied by the puppies data. So what I think I'll do is import this as puppiesData to basically alias the name as we import it. And here we can set this as puppiesData.
to basically alias the name as we import it. And here we can set this as puppies data. And so because I've named it puppies, we are already technically passing the puppies here and there everything should work the same. But now we using the state puppies value instead of just the data from the file. Let's just check that it still works. So we can remove a puppy, we can add a puppy and we can filter.
So we can remove a puppy, we can add a puppy and we can filter. Yep, everything still works. Alright, so now the magic and the key reason why we have set that as a piece of state is that we can pass to the new puppy form the setPuppies state function, which is as you can imagine, going to help us update our state data with the new puppy that we passed through the form. So let's receive here the setPuppies.
that we passed through the form. So let's receive here the set puppies. And we are going to type this as dispatch. setState action. You've been there before and an array of puppies, I just need to import the puppy type. Voila. And so now we are going to go in a form action. I can remove the commented out on submit event listener as you now appreciate that react does this work for us. And so instead of logging the data here, let's try to construct a new puppy.
Create and Add Puppy8:38
And so instead of logging the data here, let's try to construct a new Puppy. const newPuppy equals an object. And you know what? We can type this as a Puppy. This is going to help us tremendously. And so let's try to construct this new puppy data thanks to TypeScript. I can see the field that it needs. So let's start with the id. If we look at the puppies data for a second, you can see
So let's start with the id. If we look at the puppies data for a second, you can see that each puppy has an ID that increments by 1, 1, 2, 3, 4. And so essentially I think the ID can be the puppies array length. We currently have six puppies and the last puppy has an ID of six. So if you wanna add the seventh one, we could go puppies.length plus one. So let's try that puppies.length plus one.
we could go puppies.length plus one. So let's try that puppies.length plus one. Okay, then we have a name, which can be our form data do get name or here we could do the object from entries to get name and trait at the same time. But we have a field with the name of trait, but the expected field is actually vibe because we've changed it. So let's go with the manual approach.
because we've changed it. So let's go with the manual approach for form data that gets name. And here's what I mean. The vibe field needs to be form data that gets trait and I just realized that TypeScript is not happy about puppies because we only passing the set puppies. But if we want to know the length of the puppies array, we also need to pass it. So let's receive it here.
we also need to pass it. So let's receive it here. And in app.tsx we also going to pass it like that. Let's update the type to also have puppies, which is an array of puppy. And so now we should be closer. Okay, so here it's not happy because it wants a string, but we actually passing a formData entry value, but we know we want it to be a string.
but we actually passing a form data entry value, but we know we want it to be a string. And so we can cast that type with (string). So we basically telling type three that we want this to be as a string. And let's do that again as string. And so the last thing that it needs is going to be the image path. And here once again I'm going to be a bit cheeky, you can see that the image also uses the puppies
And here once again I'm going to be a bit cheeky, you can see that the image also uses the puppies that length basically. And then it's /images/ and then the ID and then .jpeg. And looking in the public directory, I have 22 of these images. So we can create another 16 puppies and it should work and use the image that sits in the folder. All right, so we're gonna go /images/puppies/length + 1 .jpeg.
All right, so we're gonna go /images/puppies length plus one that's jpeg. Okay. Whew, that was a lot. So let's try to console.log that new puppy on submit. All right, I'll scroll to the bottom and create once again, Trex loves water. He really does. And click add puppy. And we have a new puppy object that has an ID and an image path that matches our expectation of using the puppies array for number seven name vibe.
and an image path that matches our expectation of using the puppies array for number seven name vibe. And so I'm pretty confident that we can actually update the puppy state with that new puppy and it's going to re-render the puppy's list. And we should have our seventh puppy here. Let's try that. Instead of logging the puppy here, we are going to set puppies. And like often ruining the surprise copilot has showed us that we can spread the existing array of six puppies
And like often ruining the surprise copilot has showed us that we can spread the existing array of six puppies and then append the seventh puppy at the end of it. So let's save that. And we are going to create a new puppy loves peanut butter and we are going to have a lucky draw seventh image. And are you ready? We are going to add the puppy. Uh, I forgot. I really, really forgot that we had this little bunny in the middle.
I really, really forgot that we had this little bunny in the middle. Uh, awesome submission. I forgot the name of the bunny, but I remember when I received it. And so let's try another one. T Rex loves wolfing. As I literally hear wolfing outside the door, we are going to submit. Very cool. Okay, so that's react form actions in action.
we are going to submit. Very cool. Okay, so that's react form actions in action. Right now we're using fake data that lives in the file system. We even already have the image in place. But in a more realistic scenario, it would take a little bit of time for the form to submit and then the processing to happen and then the results to come back to us. So in the next lesson, we are going
and then the results to come back to us. So in the next lesson, we are going to add some intentional artificial delay and see how we can handle this pending asynchronous state that happens during a form submission. See you there.
