Check out a free preview of the full Complete Intro to React, v8 course:
The "Uncontrolled Forms" Lesson is part of the full, Complete Intro to React, v8 course featured in this preview video. Here's what you'd learn in this lesson:

Brian walks through implementing an uncontrolled form where the form data is handled by the DOM instead of a React component. To write an uncontrolled component, instead of writing an event handler for every state update, a ref can be used to get form values from the DOM.

Get Unlimited Access Now

Transcript from the "Uncontrolled Forms" Lesson

>> So we just did, that was react-query. We have one more place where we're doing an effect, that is on SearchParams.jsx. I think we all agree now that useEffect is the worst and we never wanna use it ever again. Yeah, we get on this front, cool. But we still have it here.

[00:00:24] We can get rid of it and we can also do a second thing which I wanted to teach you anyway, which is how to do an uncontrolled form. So here we have location and animal. So location, animal, we're using useState to track them individually. We're not doing anything with location and animal, other than just sending it to the API whenever we call request pet, right?

[00:00:52] It would be awesome if we didn't have to do that, we could just let the browser take care of it. And then we could just pull it out of the browser whenever we had a submit event, which you can. So let's go ahead and do that. So first of all, we reached the milestone of 09-react-query.

[00:01:14] So if you're lost, you can hop over to that one. And then we're gonna do uncontrolled forms right now. So first thing first, let's go and make our fetchSearch.js. And this is gonna be an async function fetchSearch with the query key. And then we're gonna have const (animal, location, breed) = querykey1.

[00:01:53] So there was a previous question about what happens if I have multiple things that I'm tracking? You can do something like this where you give it, instead of a individual string, you can give it an object, right? So our querykey here is gonna be an object, React Query is totally fine dealing with objects as well.

[00:02:11] Okay, and then const res = await fetch, Animal =, Animal and, Location = location. And breed = breed. Same request that we're making in our useEffect, we're just gonna do it here. If res is not okay, Then throw new Error, pet search not okay. Animal, location. Just try and make a useful error message.

[00:03:24] That's what I try and do. Okay, otherwise, you're just gonna return res.json and export, default, fetchSearch. And there you go. That is our fetchSearch function. And now we have a few things to do to searchParams.jsx. So at the top here, remove useEffect from React. We're going to import useQuery from tanstack react-query.

[00:04:08] And we're going to import fetchSearch from fetchSearch. Total side note, but obviously my organization of code here is messy at best. This is not a course in how to organize your code. Typically, what I would do here is I'd have all my fetches probably in a folder. I'd have components in a folder.

[00:04:34] I don't know, there's a million ways to organize React components. There's also a school of thought of like, I'll have details in a folder with the component and all of its fetches and all of its styles, and all of its tests, and it's all contained in one folder, that's another really good way of doing it.

[00:04:50] It's probably even better, if I'm being honest, a bunch of ways to do it. Suffice to say, do not take this as a course on how to organize your code. Okay, so we've done that inside the render at the top, delete location and breed, you stay calls. So we're gonna get rid of breed and location here.

[00:05:11] We're gonna do this as an uncontrolled form, right? We still need to keep track of animal because animal feeds into breed list. That one has to be controlled, right? So you can even do these in kind of a hybrid fashion. Well, it'll make more sense as we go.

[00:05:28] Okay, we're gonna have a requestParams function. So const requestParams, and we're just gonna keep track of that as one top level setRequestParams piece of state, useState. And the default state for that is gonna be an object, with location of being nothing, animal being nothing, and breed being nothing.

[00:06:03] So we have to do it this way, and I guess we'll get into this in just a second. But we don't want it to research every single time that a user clicks, types into location or every time that they type into or change their animal. We wanna wait until they click Submit.

[00:06:21] So we're gonna wait until they click Submit, till we set this requestParams. So that'll make sense in a second. Okay, we're gonna get rid of requestPets and the useEffects, just go and delete all of that. I'm gonna say const results = useQuery, ("search"), and we're gonna give it requestParams.

[00:06:51] And then we're gonna give it fetchSearch to do what it needs to refetch and then const pets = results. Data, pets, or empty array. We already had the no pets shown kind of thing, so we don't have to do too much for the loading screen here. Okay, so now we're not tracking location and animal.

[00:07:28] So you can actually even go and you can get rid of the value=[location], you can get rid of the onChange here. You do have to go put a name on this. Yeah, you do. Yeah, name = anim or location. The reason for this is this is how forms work.

[00:07:47] It's not about React thing. So get rid of the value here. Actually, no, you do have to do that one. Keep that one, that's fine, but I'm breed Get rid of value = breed here and make this name = "breed", and then get rid of the onChange. Okay, that's all good.

[00:08:19] That's all good. Now we need to go modify our submitEvent. So here, preventDefault, we want that. Our object that we're going to set here is gonna be this, is gonna be animal: formData, no, we have to create the formData first. So hold on. Yeah, formData = new formData with

[00:08:56] This is a browser API, it's not a React thing. You can feed it a form and it'll pull out all of the data on the form for you into an object. Const obj = animal: formData.get("animal"), and if it's not anything, then just make an empty string. And we're gonna do the same thing for breed and the same thing for location.

[00:09:35] So animal animal, breed breed, location location. Now we're gonna have an obj which is gonna have the animal, the breed, and the location, which is gonna be the current state of the form, yes. And then here, we're just gonna say, setRequestParams to be obj. Yeah, and you can drop this pets, you stayed up here as well..

[00:10:08] Do I still have errors? Yeah, and, Don't need to do this anymore. Okay, All right, make sure I'm not. It's telling you sweet lies. Let's put dog, Havanese, and click Submit. French Bulldog. Now, what's cool about this, if I hit Submit, notice how fast this is gonna be, it's instant, right?

[00:10:58] Cuz there's no waiting anymore, right? Because it's cached that request because it's seen it before. So one notice that we do not have any more location and breed being tracked by React, that's the uncontrolled form part of this. It's much more common. And I'll say more common. It's just a better idea to do it this way, unless you have some sort of data dependency like we do with animal, right?

[00:11:28] Animal, we do have to feed into this breed list. So it's important that as a tract controlled React input. And then here, we're just using the formData to make this request object, which we're using here. And then notice that we're just feeding this request object into here, right?

[00:11:51] It's critical that this doesn't change until we anticipate it to research, right? It's why we didn't just feed location directly into useQuery, because every time you were typing it, it would research, right? Because it would change the cache key. So that's why it's important that we do this little bit of song and dance to use this piece of state here and be careful to only update it.

[00:12:16] We wanted to research, right? Does that makes sense? Notice what I'm doing is loading thing, you could totally have some nice loader here. Here, we're just relying on the fact that if we provide an empty array into Results, it's just gonna show that no pets are available, right?

[00:12:35] There's a better way to do that, but for now, you know how to, you can go do it on your own time. Might be something cool for you to do afterwards. We now have no useEffect in our code, which is pretty cool, I think, right? This reads really well here of, we now have this query, it goes out to our search API and it has a nice caching mechanism, it has back off.

[00:13:05] It has error handling and has a bunch of stuff that we didn't have to code ourselves, which I think is really cool. Questions about any of this? What do you think? UseQuery, yes or no? Do you like it?
>> It's a lot of code.
>> You think that useQuery is dirtier?

[00:13:33] I disagree, I wanna hear why you think it's dirtier.
>> Just the amount of code that you replaced, that you ripped out when you took it out.
>> So, okay, it's a valid point, I wanna think about it a little bit further cuz I would say we have less code.

[00:13:59] So fetchSearch, basically this is roughly equivalent to a request pet pause, right? Back here in searchParams, we had to do more for the onSubmit event. That is an expanded amount of code for sure, but we got rid of all the useEffect which was here. And then we gotta get rid of a bunch of handling of requests.

[00:14:29] That's kind of separate, but-
>> Cuz you no longer have tracking status at all, Since we ripped out useEffect.
>> Yeah, and we don't have to track like, when for it to recall that API. It basically just handles itself based on these request keys. I'll say in this case, we had refactored enough that it's roughly probably the same amount of code.

[00:14:59] The question for you, is it more obvious for you with useQuery where that data is coming from or the useEffect? Here, I would accept that it's roughly equivalent in terms of clarity. I'll say that it's more robust this way because we get all the React query goodness that's baked into it.

[00:15:20] But I think in the fetchBreed list instance, or even the Details one, this is very clean to me, Right, as opposed to how this would be done with useEffect. This is basically one line of where all the information's coming from. Whereas with useEffect, you'd have to have the entire fetchPet business inside of Details.

[00:15:55] But I mean, if you're not into it, you're not into it. That's totally fine. You just have to leave now. I'm just kidding. [LAUGH]