How To: Controlled Forms with React
Cody Barker
Posted on March 9, 2023
Unlike JavaScript, React makes use of state in order to keep track of component changes over time. While using state does incur some added complexity, it has its benefits, particularly when it comes to creating controlled forms. We use state to keep track of the dynamic input values in our forms. So what is state exactly?
State
While props remain static, state is dynamic data. It changes over time. Setting state allows us to rerender our components without having to refresh the page or change/add props. That's why state plays an important role in how we handle forms in React. When the input, text area, or select values of a form are set with state, we have a controlled form.
In order to use state in our app, we need to import the {useState} hook from React like so.
import React, { useState } from 'react'
Next, we need to invoke our useState hook. The useState hook returns an array of 2 variables, the first being a reference to the value of our state variable, and the second a function that allows us to update that state, which begins with "set". Whatever is within the parentheses of the useState hook becomes the default value of our state. In many cases, it might simply be an empty string. An example of the useState hook might look a little something like this:
const [name, setName] = useState("")
Above, "name" is our state, and "setName" is our setter function. Because we have an empty string inside the parentheses of useState, the default value of "name" is an empty string. An important note about state: whenever we want to update state, we must pass a new object or value to our setter function. By default, we don't have access to the updated state value until the component has rerendered. If we want to update state using the current value of state, we must pass it within a callback function like so.
setName((name) => `${name} Barker`)
Finally, whenever we call set to update our state, our component and all of it's children components will rerender.
Controlled Forms
Setting up a controlled form is relatively easy. First things first, set up your form. Here's a simple example.
import React from 'react'
function Form() {
return(
<form>
<input
type="text"
name="name"
placeholder="name">
</input>
<input
type="number"
name="age"
placeholder="age">
</input>
<button type="submit">Submit</button>
</form>
)
}
export default Form
Next, import the useState hook from React.
import React, { useState } from 'react'
function Form() {
return(
<form>
<input
type="text"
name="name"
placeholder="name">
</input>
<input
type="number"
name="age"
placeholder="age">
</input>
<button type="submit">Submit</button>
</form>
)
}
export default Form
Then, call your useState hook within your component to set up state for both of your form inputs.
import React, { useState } from 'react'
function Form() {
const [name, setName] = useState("")
const [age, setAge] = useState("")
return(
<form>
<input
type="text"
name="name"
placeholder="name">
</input>
<input
type="number"
name="age"
placeholder="age">
</input>
<button type="submit">Submit</button>
</form>
)
}
export default Form
Next, set the input values equal to the corresponding state values. This will allow us to clear our input fields after submitting our form.
import React, { useState } from 'react'
function Form() {
const [name, setName] = useState("")
const [age, setAge] = useState("")
return(
<form>
<input
type="text"
name="name"
placeholder="name"
value={name}>
</input>
<input
type="number"
name="age"
placeholder="age"
value={age}>
</input>
<button type="submit">Submit</button>
</form>
)
}
export default Form
Now we need to set up event listeners to update state and rerender our component whenever the user enters something into our input fields.
import React, { useState } from 'react'
function Form() {
const [name, setName] = useState("")
const [age, setAge] = useState("")
function handleName(e) {
setName(e.target.value)
}
function handleAge(e) {
setAge(e.target.value)
}
return(
<form>
<input
onChange={handleName}
type="text"
name="name"
placeholder="name"
value={name}>
</input>
<input
onChange={handleAge}
type="number"
name="age"
placeholder="age"
value={age}>
</input>
<button type="submit">Submit</button>
</form>
)
}
export default Form
At this point, we've controlled our inputs, so from here it all depends on what we want to do when we submit. If we just wanted to add these values to an API, we could add a submit event listener to the form and make a fetch post request to the database. That might look something like this.
import React, { useState } from 'react'
function Form() {
const [name, setName] = useState("")
const [age, setAge] = useState("")
function handleName(e) {
setName(e.target.value)
}
function handleAge(e) {
setAge(e.target.value)
}
function handleSubmit(e) {
e.preventDefault()
const newUser = {
username: name,
userAge: age
}
fetch('http://localhost:3001/users', {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(newUser)
})
.then(r => r.json())
.then(user => console.log(user))
.then(
setName(""),
setAge("")
)
}
return(
<form onSubmit={handleSubmit}>
<input
onChange={handleName}
type="text"
name="name"
placeholder="name"
value={name}>
</input>
<input
onChange={handleAge}
type="number"
name="age"
placeholder="age"
value={age}>
</input>
<button type="submit">Submit</button>
</form>
)
}
export default Form
To recap, whenever a user changes the text within our form input fields, the setter function is called in the event listener, updating our states by setting them equal to the input values. Every state change rerenders the component without a page refresh, updating everything immediately. How nice!
When we submit our form, we create a new object called newUser, setting the key value pairs using state.
We then make a fetch request to our example API, console.log the response which is our newUser, and then clear the input fields by setting state back to empty strings. If done correctly, our newUser will be added to the database.
Remember, we aren't just limited to controlling forms with inputs. We can also control form elements like and with state in very much the same way!
Posted on March 9, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.