One of the first things people learn in React is how to control a form. Unfortunately, they never learn that they probably shouldn't. Let's see how we can use the universally available FormData API to manage our forms instead. And to be fair, We'll also talk about some cases where a controlled form does make sense.
What is a controlled form?
Just so we're all clear on what I'm talking about, a controlled form is one that has controlled inputs. And a controlled input is when the value of the input comes from state and there is an onChange
to update that state. It looks like this:
What's the problem?
In a word: boilerplate. That's a lot of code for the simple task of submitting a form. Users of other frameworks are quick to point out the absurd number of lines needed to mimic default behavior of a form. Look at that exact same example in Svelte code:
I'm a fan of the way React does a lot of things, but I will also admit this is not React's finest moment. But what if I told you that's because you aren't supposed to control forms like this?
Using FormData
Crazy idea here, but controlled forms are for situations where you need control and those moments are rarer than you'd think. Instead, we can use the browser's built in FormData API to clean up and simplify our code. I'll explain it in a minute but just look how much simpler our React code is:
Unsurprisingly, losing the change handlers, initial values, and state hooks really simplifies our code.
Getting form data
Previously, we had to use a state hook to track our form data, but that's overkill. Simply apply name
fields to the inputs, then the form is always aware of the key-value pairs. The key is the name
property and the value
is the input's value. This even works for things like checkboxes and radio buttons. Never use a for loop to find the value of radio or checkboxes again!
And just like a click handler sets the e.target
to the clicked button, a submit handler will give you the form as the e.target
. So we simply grab any data we need in our handler. No need for any sort of DOM selection methods, a big anti-pattern in React, all we need is provided by the event.
No more state and change handlers
Before we talk more about what we added, let's discuss what we removed. Since we only care about the value of the inputs on the submit event and not before, there's no need for the change handler to update state. The “state” is just the form itself. Our JSX is now shorter since we don't need the value
and onChange
attributes.
Converting form data to an object
This is the piece that uses the FormData API. This new line is really the only tricky part:
We can make that more readable by doing it like this:
I wouldn't recommend writing it that way in your application, but there it is for now. Our first task is simply getting access to our form from e.target
like we discussed earlier. Then, we need to convert it into a new instance of a formData
object. So we call new FormData
and pass in our form element.
Of course, this formData
object has a lot of cool methods on it that we aren't using here, but for now we just want to make a plain JS object so we can shoot it to our server. In order to do that, use the Object.fromEntries method. That's it! For a form as simple as this, you're done.
Resetting the form
No need for something like initForm
object, all we need to do is call the form.reset
method that's native to form elements. It wipes the form inputs to whatever the default values were.
When should you control forms?
There are of course valid reasons to control forms. The most obvious cases are when you need more rigorous validation or formatting. Like if you want to automatically uppercase every letter or apply a very limited set of phone number patterns. Though there again, heads up that you can apply automatic validation on form submission in HTML. Here's an email input that only allows the form to submit if the email has the proper domain:
The other obvious reason to control a form is if that data is needed elsewhere in your application. Like if you're lifting up the state, or running some debounce function on inputs. And of course, if there's ever some weird edge case that pops up where controlling is the answer: do it!
Think before you code
The point is, sometimes you'll want to control a form, but it shouldn't just be the default anymore. Think about the state of your application and always ask yourself if a form needs controlling. It will save your app a lot of time and complexity if you wait before adding all those hooks and state.
Examples like form submission truly show the power of web standards. The FormData API has been in all major browsers for years now, which means no matter what framework you use, this technique will work. For all the Jrs. out there wondering what skills they should learn, web standard APIs can't really ever go out of style.
Happy coding everyone,
Mike