What are controlled and uncontrolled components?

👨‍💻 Frontend Developer 🟠 May come up 🎚️ Medium
#React

Brief Answer

Controlled components are components whose state is managed by React through state. Uncontrolled components are components whose state is managed directly by the DOM, without React’s involvement.

Controlled components:

  • State managed through React state
  • Value always matches state
  • Allow validation and real-time manipulations

Uncontrolled components:

  • State managed directly by DOM
  • Value obtained through refs
  • Less code, closer to traditional HTML

Key rule: In most cases, use controlled components for better control and validation! 🎯


Full Answer

Imagine you’re driving a car. Controlled components are like adaptive cruise control that constantly monitors speed. Uncontrolled components are like mechanical cruise control that simply keeps the gas pedal pressed.

What are controlled components

Controlled components are components whose value is controlled by React:

// Controlled component
function ControlledInput() {
  const [value, setValue] = useState('');
  
  return (
    <input 
      type="text" 
      value={value} 
      onChange={(e) => setValue(e.target.value)} 
    />
  );
}

What are uncontrolled components

Uncontrolled components are components whose value is managed directly by the DOM:

// Uncontrolled component
function UncontrolledInput() {
  const inputRef = useRef(null);
  
  const handleSubmit = () => {
    console.log(inputRef.current.value);
  };
  
  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleSubmit}>Get value</button>
    </div>
  );
}

Approach comparison

CharacteristicControlledUncontrolled
State managementThrough React stateThrough DOM
ValidationReal-timeOn submit
CodeMoreLess
FlexibilityHighLimited
PerformanceCan be lowerUsually higher

When to use controlled components

Use controlled components when:

  1. Real-time validation is needed
  2. Value depends on other states
  3. Need to programmatically change value
  4. Instant reaction to changes is required
// Example with validation
function EmailInput() {
  const [email, setEmail] = useState('');
  const [error, setError] = useState('');
  
  const validateEmail = (value) => {
    if (!value.includes('@')) {
      setError('Invalid email');
    } else {
      setError('');
    }
  };
  
  return (
    <div>
      <input 
        type="email" 
        value={email} 
        onChange={(e) => {
          setEmail(e.target.value);
          validateEmail(e.target.value);
        }}
        style={{ borderColor: error ? 'red' : 'green' }}
      />
      {error && <span style={{ color: 'red' }}>{error}</span>}
    </div>
  );
}

When to use uncontrolled components

Use uncontrolled components when:

  1. Need to integrate with libraries that work with DOM
  2. Component rarely changes
  3. Need to minimize code
  4. Integration with non-React code
// Example with third-party library integration
function DatePicker() {
  const dateRef = useRef(null);
  
  useEffect(() => {
    // Initialize third-party library
    new SomeDatePickerLibrary(dateRef.current);
  }, []);
  
  return <input type="text" ref={dateRef} />;
}

Common mistakes

1. Mixing approaches

// ❌ Error - mixing controlled and uncontrolled
function BadComponent() {
  const [value, setValue] = useState('');
  
  return (
    // Both value and defaultValue specified - conflict!
    <input 
      value={value} 
      defaultValue="initial" 
      onChange={(e) => setValue(e.target.value)} 
    />
  );
}
 
// ✅ Correct - choose one approach
function GoodControlled() {
  const [value, setValue] = useState('initial');
  return (
    <input 
      value={value} 
      onChange={(e) => setValue(e.target.value)} 
    />
  );
}
 
function GoodUncontrolled() {
  return <input defaultValue="initial" />;
}

2. Overusing uncontrolled components

// ❌ Wrong - when control is needed
function BadForm() {
  // Impossible to validate or reset form
  return (
    <form>
      <input type="text" name="username" ref={useRef()} />
      <input type="email" name="email" ref={useRef()} />
      <button type="submit">Submit</button>
    </form>
  );
}
 
// ✅ Correct - when control is needed
function GoodForm() {
  const [username, setUsername] = useState('');
  const [email, setEmail] = useState('');
  
  const handleSubmit = (e) => {
    e.preventDefault();
    // Validation and data submission
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text" 
        value={username} 
        onChange={(e) => setUsername(e.target.value)} 
      />
      <input 
        type="email" 
        value={email} 
        onChange={(e) => setEmail(e.target.value)} 
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Summary

Controlled and uncontrolled components are like two different driving styles! 🚗

  • Controlled - like automatic with adaptive cruise control: full control, comfort, but a bit more complex
  • Uncontrolled - like manual: simpler, faster, but less control

When to choose what:

  • Need validation and control? Controlled! ✅
  • Simple integration with third-party libraries? Uncontrolled! ✅

Practical rule: Start with controlled components - they solve 90% of form tasks! If you encounter integration difficulties, then consider uncontrolled ones.

Understanding these concepts will help you create more predictable and user-friendly forms in your React applications! 💪


Want more useful React articles? Subscribe to EasyAdvice, bookmark the site and level up every day! 🚀