How to Implement Uncontrolled Components in React
Uncontrolled components in React offer a straightforward way to
handle form data by letting the DOM manage the form state rather than
keeping it in React state. This article explains the core concepts of
uncontrolled components, demonstrates how to implement them using the
useRef hook, and highlights the scenarios where they are
most effective.
What are Uncontrolled Components?
In React, an uncontrolled component is a component where form data is
handled by the DOM itself. Instead of writing an event handler for every
state update (like you do with controlled components using
useState), you use a ref to pull the values
from the DOM when you need them—typically when the form is
submitted.
This approach results in less code and can be easier to integrate with non-React libraries, though it offers less control over real-time validation and conditional formatting.
Implementing an Uncontrolled Component
To implement an uncontrolled component in React, you will use the
useRef hook to access the input elements directly.
Here is a step-by-step implementation of a simple form using uncontrolled components:
import React, { useRef } from 'react';
function UncontrolledForm() {
// 1. Create a ref to store the DOM element
const nameInputRef = useRef(null);
const emailInputRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
// 2. Access the value directly from the DOM ref
const nameValue = nameInputRef.current.value;
const emailValue = emailInputRef.current.value;
console.log('Submitted Name:', nameValue);
console.log('Submitted Email:', emailValue);
};
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name">Name:</label>
{/* 3. Attach the ref to the input element */}
<input
type="text"
id="name"
ref={nameInputRef}
defaultValue="John Doe"
/>
</div>
<div>
<label htmlFor="email">Email:</label>
<input
type="email"
id="email"
ref={emailInputRef}
/>
</div>
<button type="submit">Submit</button>
</form>
);
}
export default UncontrolledForm;Key Steps in the Implementation:
- Create the Ref: Initialize a ref using
useRef(null)for each input field you want to track. - Assign the Ref: Pass the ref to the React element
using the
refattribute (e.g.,ref={nameInputRef}). - Set Default Values: Use the
defaultValueattribute instead ofvalueto set an initial value. Usingvalueon an uncontrolled input will make the field read-only and generate a console warning. - Access the Value: Retrieve the input value during
an event (like
onSubmit) usingrefName.current.value.
Handling File Inputs
In React, file inputs (<input type="file" />) are
always uncontrolled components because their value can only be set by a
user, not programmatically by React.
Here is how you handle a file input using refs:
import React, { useRef } from 'react';
function FileUpload() {
const fileInputRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
const files = fileInputRef.current.files;
if (files.length > 0) {
console.log(`Selected file: ${files[0].name}`);
}
};
return (
<form onSubmit={handleSubmit}>
<input type="file" ref={fileInputRef} />
<button type="submit">Upload</button>
</form>
);
}When to Use Uncontrolled Components
While controlled components are recommended for most forms in React, uncontrolled components are highly useful in specific scenarios:
- Simple Forms: When you only need to collect form data upon submission and do not require instant field validation or UI changes based on input.
- Performance Optimization: In massive forms where updating React state on every keystroke causes performance lag.
- Integrating with Third-Party DOM Libraries: When wrapping jQuery or other non-React libraries that manage their own state.
- File Inputs: As mentioned, file inputs must be implemented as uncontrolled components.