React Controlled vs Uncontrolled Components

When managing forms, developers can choose between controlled and uncontrolled components, each offering unique advantages and ways of handling user input data.

What Are Controlled Components?

Controlled components in React refer to form elements that are fully managed by the React component’s state. This means that the value of each form element, such as an input field, is bound to a state variable.

When the user interacts with the form element, any change in the value updates the state and the updated state re-renders the component.

In controlled components, React is the single source of truth for form values. This approach allows for precise control over the form data, making it easier to validate and manipulate as needed.

Key Characteristics of Controlled Components:

  • State-Driven: The form element values are stored in the component’s state.
  • OnChange Event Handling: Changes in form elements are tracked via onChange handlers, which update the state.
  • Real-Time Validation: Because the state holds the form data, it’s easy to validate input values as the user types.

Example of a Controlled Component

Below is a simple example of a controlled component where a text input field is bound to a username state.

import React, { useState } from 'react';

function ControlledComponent() {
const [username, setUsername] = useState('');

const handleChange = (event) => {
setUsername(event.target.value); // Updates the state with input value
};

const handleSubmit = (event) => {
event.preventDefault();
alert(`Submitted Username: ${username}`);
};

return (
<form onSubmit={handleSubmit}>
<label>
Username:
<input type="text" value={username} onChange={handleChange} />
</label>
<button type="submit">Submit</button>
</form>
);
}

export default ControlledComponent;

In this example:

  • username is a state variable representing the current value of the input field.
  • handleChange updates the state whenever the input value changes.
  • The input’s value attribute is set to the username state, meaning any update to username re-renders the input with the updated value.

Advantages of Controlled Components:

  • Complete Control: Allows direct control over form data, making it easy to perform real-time validation and formatting.
  • Consistency: The form values are always in sync with the state, reducing the risk of inconsistencies.
  • Easier Debugging: Since the data flows one way (from the state to the input), it’s easier to track and debug.

What Are Uncontrolled Components?

Uncontrolled components, unlike controlled ones, do not rely on the component’s state to manage form data. Instead, form elements maintain their own internal state.

This means the form values are accessed directly from the DOM using a ref, which is a reference to the actual HTML element.

In uncontrolled components, the React component does not manage the form element values.

This approach can be simpler for small forms or when only the final form values are needed (such as on submission), rather than tracking every input change.

Key Characteristics of Uncontrolled Components:

  • DOM-Driven: Form data is stored within the DOM, not in the React state.
  • Refs for Access: Refs are used to access the form element values when needed, rather than constantly updating the state.
  • Less Control Over Each Change: While efficient for final form submission, uncontrolled components are less suited for complex validations during user input.

Example of an Uncontrolled Component

Below is an example where a form’s input field uses a ref to get its value upon submission, without tracking changes in the component’s state.

import React, { useRef } from 'react';

function UncontrolledComponent() {
const usernameRef = useRef(null);

const handleSubmit = (event) => {
event.preventDefault();
alert(`Submitted Username: ${usernameRef.current.value}`);
};

return (
<form onSubmit={handleSubmit}>
<label>
Username:
<input type="text" ref={usernameRef} />
</label>
<button type="submit">Submit</button>
</form>
);
}

export default UncontrolledComponent;

In this example:

  • The usernameRef is a reference to the input element.
  • When the form is submitted, the value of the input is accessed through usernameRef.current.value.
  • There is no onChange handler or state tracking.

Advantages of Uncontrolled Components:

  • Simpler Code: Suitable for small forms where only final values are needed.
  • Less State Management: Reduces the need for state handling, which can simplify the component structure.
  • Performance-Friendly: Reduces re-rendering since the component’s state is not affected by every user input.

Key Differences Between Controlled and Uncontrolled Components

AspectControlled ComponentsUncontrolled Components
State ManagementManaged by React stateManaged by the DOM
Event HandlingUses onChange to update stateUses ref to access values when needed
Real-Time ValidationEasy to implement during input changesMore complex, requires manual handling
Data SourceReact component stateDOM elements directly
Use CaseIdeal for forms requiring validation, formatting, and controlIdeal for simple forms with minimal validation needs

Controlled vs. Uncontrolled: Choosing the Right Approach

  • Use Controlled Components When:
    • You need to validate user input in real time.
    • The form requires complex behavior, such as conditional rendering or formatting.
    • You want to keep the form data consistent and synchronized with other components.
  • Use Uncontrolled Components When:
    • The form is simple, and you only need the final value (e.g., on form submission).
    • Real-time validation and data manipulation are not necessary.
    • You want to reduce re-renders and improve performance in larger forms.

Example: Combining Controlled and Uncontrolled Components

Here’s an example where a form has a controlled email input and an uncontrolled age input, demonstrating how you can mix these two approaches.

import React, { useState, useRef } from 'react';

function MixedForm() {
const [email, setEmail] = useState('');
const ageRef = useRef(null);

const handleEmailChange = (e) => setEmail(e.target.value);

const handleSubmit = (e) => {
e.preventDefault();
alert(`Email: ${email}, Age: ${ageRef.current.value}`);
};

return (
<form onSubmit={handleSubmit}>
<label>
Email:
<input type="email" value={email} onChange={handleEmailChange} />
</label>
<br />
<label>
Age:
<input type="number" ref={ageRef} />
</label>
<br />
<button type="submit">Submit</button>
</form>
);
}

export default MixedForm;

In this example:

  • The email input is a controlled component managed by state (email).
  • The age input is an uncontrolled component accessed using a ref (ageRef).
  • The form combines the ease of validation with controlled components and the simplicity of uncontrolled components for fields that do not require validation.

Leave a Comment