Swiftorial Logo
Home
Swift Lessons
AI Tools
Learn More
Career
Resources

React FAQ: Top Questions

14. What are Controlled and Uncontrolled Components?

In React, forms and input elements (like <input>, <textarea>, <select>) can be managed in two primary ways: as **Controlled Components** or **Uncontrolled Components**. The choice depends on how you want to handle the input's state and data flow.

Controlled Components:

  • Definition: A controlled component is an input form element whose value is controlled by React state. The state becomes the "single source of truth" for the input's value.
  • How it works:
    • The input's value (or checked for checkboxes) is set by a state variable.
    • An onChange event handler updates the state variable whenever the input value changes.
    • This creates a two-way data binding: the UI reflects the state, and user input updates the state.
  • Pros:
    • Easier to validate input.
    • Easier to manipulate or transform input values (e.g., format text as the user types).
    • Easier to manage form submission and reset.
    • Predictable behavior and easier debugging.
  • Cons:
    • Requires more boilerplate code (state, onChange handler for each input).
    • Can lead to performance issues with very large forms if not optimized (though typically negligible).

Uncontrolled Components:

  • Definition: An uncontrolled component is an input form element whose value is managed by the DOM itself, rather than by React state.
  • How it works:
    • You typically use a ref to get direct access to the DOM element.
    • The input's value is retrieved directly from the DOM when needed (e.g., on form submission).
    • It behaves more like traditional HTML forms.
  • Pros:
    • Less boilerplate code for simple inputs.
    • Can be useful for integrating with non-React code or third-party DOM libraries.
  • Cons:
    • Harder to validate input in real-time.
    • Less control over the input's value.
    • Harder to reset the form programmatically.
    • Can be less "React-idiomatic."

In most modern React applications, **controlled components are preferred** because they provide more control and predictability over form data. Uncontrolled components are generally used for simpler cases or when direct DOM access is specifically required.


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

// --- Controlled Component Example ---
function ControlledInput() {
  const [inputValue, setInputValue] = useState('');

  const handleChange = (event) => {
    setInputValue(event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault(); // Prevent default form submission behavior
    alert(`Controlled Input Value: ${inputValue}`);
  };

  return (
    <div className="swf-lsn-card p-4 mb-4">
      <h3>Controlled Component</h3>
      <form onSubmit={handleSubmit}>
        <label className="block text-sm font-medium text-gray-700">
          Name:
          <input
            type="text"
            value={inputValue} {/* Value is controlled by state */}
            onChange={handleChange} {/* State is updated on change */}
            className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
          />
        </label>
        <button
          type="submit"
          className="px-4 py-2 mt-3 rounded-md bg-blue-600 text-white"
        >
          Submit Controlled
        </button>
      </form>
      <p className="mt-2 text-sm text-gray-500">Live Value: {inputValue}</p>
    </div>
  );
}

// --- Uncontrolled Component Example ---
function UncontrolledInput() {
  const inputRef = useRef(null); // Create a ref

  const handleSubmit = (event) => {
    event.preventDefault();
    // Access value directly from the DOM using the ref
    alert(`Uncontrolled Input Value: ${inputRef.current.value}`);
  };

  return (
    <div className="swf-lsn-card p-4">
      <h3>Uncontrolled Component</h3>
      <form onSubmit={handleSubmit}>
        <label className="block text-sm font-medium text-gray-700">
          Email:
          <input
            type="email"
            ref={inputRef} {/* Attach the ref to the input */}
            defaultValue="test@example.com" {/* Initial value set here */}
            className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-green-300 focus:ring focus:ring-green-200 focus:ring-opacity-50"
          />
        </label>
        <button
          type="submit"
          className="px-4 py-2 mt-3 rounded-md bg-green-600 text-white"
        >
          Submit Uncontrolled
        </button>
      </form>
      <p className="mt-2 text-sm text-gray-500">
        Value is read only on submit.
      </p>
    </div>>
  );
}

// --- App Component ---
function App() {
  return (
    <div className="app-container swf-lsn-container">
      <h1>Controlled vs. Uncontrolled Components</h1>
      <ControlledInput />
      <UncontrolledInput />
      <div className="swf-lsn-list mt-8">
        <p>
          This example demonstrates the key differences between controlled and
          uncontrolled components for form inputs.
        </p>
      </div>
    </div>
  );
}

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

Explanation of the Code:

  • ControlledInput Component:
    • Uses useState to manage inputValue.
    • The <input> element's value prop is bound to inputValue (value={inputValue}).
    • The onChange handler (handleChange) updates inputValue with the current value from the input field. This ensures that React state is always the source of truth.
    • The "Live Value" paragraph updates immediately as you type, demonstrating real-time control.
  • UncontrolledInput Component:
    • Uses useRef to create a ref (inputRef). This ref is then attached to the <input> element (ref={inputRef}).
    • defaultValue="test@example.com" sets the initial value, but after that, the DOM manages the input's value directly.
    • On form submission (handleSubmit), the input's value is accessed directly from the DOM via inputRef.current.value. React does not re-render the component as you type in this input.
  • This example clearly illustrates the fundamental difference in how input values are managed and accessed in controlled versus uncontrolled components.