When you’re first learning React, one of the key concepts to understand is the difference between controlled and uncontrolled components. Below we’ll dive into the key differences between the two when you’re creating a form, as well as the benefits of controlled forms so you’ll understand when to use one over the other.
What are Controlled Forms?
Controlled forms are forms whose values are controlled by state. These values include input fields, text areas, and select dropdowns. As a quick reminder, state is used when a component needs to keep track of information that might change over time (eg. form inputs, storing data fetched from an API, toggling visibility of components, etc).
In the example of the controlled form below, the “first”, “last” and “city” inputs are controlled by state. The handleChange function is triggered whenever there’s a change in any of the form inputs. It updates the “formData” state by calling the setFormData function and passing in the new data as an argument – spreading the existing state (making a copy of it) and adding the new key/value pairs.
You’ll notice on the elements that each element has a “name” attribute that corresponds to the key in the “formData” state and a “value” attribute that corresponds to the value in state. This setup enables the inputs to be controlled by state.
Eg. Controlled Form:
import { useState } from "react";
const initialState = {
first: "",
last: "",
city: ""
}
const ControlledForm = () => {
const [formData, setFormData] = useState(initialState)
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
})
}
const handleSubmit = (e) => {
e.preventDefault()
// Handle form submit
}
return (
<div>
<form onSubmit={handleSubmit} onChange={handleChange}>
<input type="text" name="first" value={formData.first} />
<input type="text" name="last" value={formData.last} />
<input type="text" name="city" value={formData.city} />
<button type="submit">Submit</button>
</form>
</div>
)
}
export default ControlledForm;
Note: It's typical to add the onChange to each input element. In the code above, I've taken advantage of event delegation and added it to the form element instead. This setup works in the same way as if you had added the onChange to the input, but makes the code DRYer. However, it's important to note that you will see an error message in your console (which you can ignore), since it's not the typical setup.
What are Uncontrolled Forms?
In contrast, uncontrolled forms do not use state to manage their form data. They use the browser’s DOM directly to handle the form inputs and to retrieve the form data.
To update our example above to an uncontrolled form, we would need to handle the form submission using references to the input fields, using the useRef hook instead of useState. In an uncontrolled form, each input needs a “ref” attribute associated with it. When the form is submitted and the “handleSubmit” function is called, the values of the inputs are retrieved using the “refs”.
Eg. Controlled Form:
import { useRef } from "react";
const UncontrolledForm = () => {
const firstRef = useRef("")
const lastRef = useRef("")
const cityRef = useRef("")
const handleSubmit = (e) => {
e.preventDefault()
// Access form data using refs
const formData = {
first: firstRef.current.value,
last: lastRef.current.value,
city: cityRef.current.value
}
// Handle form submit
// Reset the form fields
e.target.reset()
}
return (
<form onSubmit={handleSubmit} onChange={handleChange}>
<input type="text" name="first" ref={firstRef} />
<input type="text" name="last" ref={lastRef} />
<input type="text" name="city" ref={cityRef} />
<button type="submit">Submit</button>
</form>
)
}
export default UncontrolledForm;
Benefits of Using Controlled Forms
In React it’s best practice to use controlled forms instead of uncontrolled forms wherever possible. Here are some of the key benefits of using controlled forms:
1. Easier data sharing with react components
- Since the form data is managed through state, other components can easily access this data through props if needed. This creates a “single source of truth” for the form data.
2. Predictable data management and flow using state
- Handling data through state is easier and more predictable than relying on a browser’s DOM for state management. Using the DOM to manage state can also lead to unexpected behaviors.
- With controlled forms, it can be easier to understand how data flows through your application since all changes to the form data are managed through state and are passed down to child components through props.
3. Increased data consistency through the use of immutable data
- In React, state should be treated as immutable, meaning it can’t be modified directly. When changes are made to the form data, we create a new state object with the updated form data.
- Since state changes are more predictable and traceable than data directly manipulated in the DOM, it’s easier to ensure data consistency in controlled forms.
4. Easier testing, debugging, data validation, and error handling
- Testing, debugging, data validation, and error handling are all easier to implement on controlled forms since all of the form data and state changes are managed within React components.
- Uncontrolled forms can sometimes need additional testing to ensure the React components and the DOM-managed state are interacting correctly.
As a final note, there are a few specific situations where you will need to use an uncontrolled form. For example, if you’re integrating with non-React code or third-party libraries that use traditional DOM manipulation or if you have a file upload as an input in your form.
Sources:
Top comments (0)