Swiftorial Logo
Home
Swift Lessons
Tutorials
Learn More
Career
Resources

React FAQ: Top 75 Questions

8. What are controlled vs. uncontrolled components in React forms?

When dealing with forms in React, handling user input is crucial. React provides two main approaches for form components: **controlled components** and **uncontrolled components**, differing in how they manage form data.

Feature Controlled Components Uncontrolled Components
Data Handling Form data is handled by the React component's state. Form data is handled by the DOM itself.
Value Source The `value` of the form input is always driven by React state. The `value` of the form input is managed by the browser.
Updates Every state update triggers a re-render, keeping the input value in sync with the state. Updates happen directly in the DOM; React needs to be told to read the value (e.g., on form submission).
Control React component has full control over the input field (its value, validation, etc.). Less control for React; relies on DOM behavior.
Common Use Case Most common approach for modern React forms. Ideal for real-time validation, dynamic inputs, manipulating input value. Simpler for very basic forms where you only need the value once (e.g., on submit), or when integrating with non-React code.
Mechanism Input element's `value` prop is set by state, and `onChange` handler updates that state. Input element's `defaultValue` (for initial value) and `ref` to read the current value.
Complexity Slightly more boilerplate (state, `onChange` handler) but offers more power. Less boilerplate but less power and more direct interaction with DOM.

import React, { useState, useRef } from 'react';
import ReactDOM from 'react-dom/client';

// Controlled Component Example
function ControlledForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault(); // Prevent default form submission
    alert(`Controlled Form Submitted:\nName: ${name}\nEmail: ${email}`);
    // Clear form after submission
    setName('');
    setEmail('');
  };

  return (
    <div>
      <h3>Controlled Component</h3>
      <form onSubmit={handleSubmit}>
        <label>
          Name:
          <input
            type="text"
            value={name} {/* Value is controlled by React state */}
            onChange={(e) => setName(e.target.value)} {/* Updates state on every change */}
          />
        </label>
        <br />
        <label>
          Email:
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
        </label>
        <br />
        <button type="submit">Submit Controlled</button>
      </form>
      <p>Current Name (state): {name}</p>
    </div>
  );
}

// Uncontrolled Component Example
function UncontrolledForm() {
  const nameRef = useRef(null); // Create a ref for the name input
  const emailRef = useRef(null); // Create a ref for the email input

  const handleSubmit = (event) => {
    event.preventDefault();
    // Access input values directly via refs
    const name = nameRef.current.value;
    const email = emailRef.current.value;
    alert(`Uncontrolled Form Submitted:\nName: ${name}\nEmail: ${email}`);
    // You'd typically not clear inputs this way for uncontrolled
    // If you need to clear, you might reset the form or manipulate DOM directly
    // nameRef.current.value = ''; // Not recommended practice
  };

  return (
    <div>
      <h3>Uncontrolled Component</h3>
      <form onSubmit={handleSubmit}>
        <label>
          Name:
          <input
            type="text"
            defaultValue="Default Name" {/* Initial value, not controlled by React state */}
            ref={nameRef} {/* Attach ref to the input */}
          />
        </label>
        <br />
        <label>
          Email:
          <input
            type="email"
            defaultValue="default@example.com"
            ref={emailRef}
          />
        </label>
        <br />
        <button type="submit">Submit Uncontrolled</button>
      </form>
    </div>
  );
}

function App() {
  return (
    <div>
      <ControlledForm />
      <hr />
      <UncontrolledForm />
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root-forms'));
root.render(<App />);
                    

Explanation of the Code:

  • **`ControlledForm` (Controlled Component):**
    • The `name` and `email` input values are tied to `useState` hooks.
    • The `value` prop of the input is set to the state variable (`value={name}`).
    • The `onChange` handler updates the state (`setName(e.target.value)`) whenever the input changes. This creates a "single source of truth" for the input's value: the React state.
    • This allows for immediate access to the current input value, real-time validation, and easy manipulation of the input programmatically.
  • **`UncontrolledForm` (Uncontrolled Component):**
    • The input fields use `defaultValue` for initial values (if any) and are *not* tied to React state with a `value` prop.
    • `useRef` hooks are used to get a direct reference to the DOM input elements (`nameRef.current`, `emailRef.current`).
    • When the form is submitted, the values are read directly from the DOM elements using `nameRef.current.value`.
    • React has less control over the input's value in real-time, only interacting with it when explicitly told (e.g., on submit).

For most React applications, **controlled components are the preferred and recommended approach** because they offer more predictable behavior, easier debugging, and greater control over form data and user interactions.