REACT Lesson 13 – Controlled Components | Dataplexa
LESSON 13

Controlled Components

Build form inputs where React controls every keystroke, creating predictable and powerful user interactions.

A controlled component is like having a direct telephone line to your form inputs. Instead of form elements managing their own state, React becomes the single source of truth. Think of it as React holding the steering wheel while users provide the directions. The difference becomes clear when you compare how regular HTML and React handle form inputs. HTML inputs remember their own values. React controlled components ask React what their value should be on every render.

Understanding Control

Regular HTML inputs work independently. Type in a text field and the browser stores that value internally. React controlled components work differently — they ask React for their current value and report changes back to React immediately.
// Regular HTML - input controls itself
// <input type="text" />

// React controlled - React controls input
function SearchBar() {
  const [searchTerm, setSearchTerm] = useState('');

  return (
    <input 
      type="text"
      value={searchTerm}
      onChange={(e) => setSearchTerm(e.target.value)}
    />
  );
}
DataFlow Dashboard

What just happened?

React controls the input's value through state. Every keystroke triggers onChange, updates state, and re-renders with the new value. Try typing to see the controlled behavior.

The key insight: controlled components create a loop. User types → onChange fires → state updates → component re-renders → input shows new value. This loop happens so fast it feels instant but gives React complete control.

Building DataFlow Filters

The DataFlow team needs a filter system where users can search transactions and filter by category. Controlled components make this straightforward because React can validate, transform, and sync multiple inputs.
function TransactionFilters() {
  const [search, setSearch] = useState('');
  const [category, setCategory] = useState('all');
  const [minAmount, setMinAmount] = useState('');

  return (
    <div style={{padding: '20px', background: '#f8fafc'}}>
      <input 
        type="text"
        placeholder="Search transactions..."
        value={search}
        onChange={(e) => setSearch(e.target.value)}
      />
      
      <select value={category} onChange={(e) => setCategory(e.target.value)}>
        <option value="all">All Categories</option>
        <option value="revenue">Revenue</option>
        <option value="expense">Expense</option>
      </select>
    </div>
  );
}
DataFlow Dashboard

What just happened?

Three controlled inputs work independently but could easily be combined for filtering. Each has its own state variable and update function. Try changing values to see how React tracks everything.

Notice how each input needs both value and onChange props. The value tells the input what to display. The onChange tells React how to update when users interact.

Validation and Transformation

Controlled components shine when you need to validate or transform user input. Since React controls the value, you can intercept changes and modify them before they reach the input.
function PriceInput() {
  const [price, setPrice] = useState('');

  const handlePriceChange = (e) => {
    const value = e.target.value;
    
    // Only allow numbers and one decimal point
    if (value === '' || /^\d*\.?\d*$/.test(value)) {
      setPrice(value);
    }
  };

  return (
    <div>
      <label>Product Price: $</label>
      <input 
        type="text"
        value={price}
        onChange={handlePriceChange}
        placeholder="0.00"
      />
      <p>Valid price: {price ? `$${price}` : 'Enter amount'}</p>
    </div>
  );
}
DataFlow Dashboard

What just happened?

The input validates every keystroke with a regex pattern. Invalid characters never reach the state, so they never appear in the input. Try typing letters or multiple decimal points - React blocks them.

This validation approach works because React controls the input value. Invalid input gets rejected before it updates state, so the input never shows invalid characters. Users get immediate feedback without submitting forms.

Multiple Input Types

DataFlow's settings panel needs various input types - text, checkboxes, radio buttons, and selects. Controlled components work consistently across all form elements with the same pattern.
function DashboardSettings() {
  const [theme, setTheme] = useState('light');
  const [notifications, setNotifications] = useState(true);
  const [updateFreq, setUpdateFreq] = useState('5min');

  return (
    <div>
      {/* Radio buttons */}
      <label>
        <input 
          type="radio" 
          name="theme"
          value="light"
          checked={theme === 'light'}
          onChange={(e) => setTheme(e.target.value)}
        />
        Light Theme
      </label>
      
      {/* Checkbox */}
      <label>
        <input 
          type="checkbox"
          checked={notifications}
          onChange={(e) => setNotifications(e.target.checked)}
        />
        Enable Notifications
      </label>
    </div>
  );
}
DataFlow Dashboard

What just happened?

Different input types use different props for control. Radio buttons use checked and compare values. Checkboxes use e.target.checked. But the pattern stays the same - React controls everything.

The key difference: checkboxes and radio buttons use checked instead of value. Checkboxes give you e.target.checked (boolean). Text inputs give you e.target.value (string).

Form Submission

Controlled components make form submission predictable. Since React already holds all form data in state, you can validate and submit without reading DOM elements.
function AddTransaction() {
  const [amount, setAmount] = useState('');
  const [description, setDescription] = useState('');
  const [category, setCategory] = useState('revenue');

  const handleSubmit = (e) => {
    e.preventDefault();
    
    if (!amount || !description) {
      alert('Please fill all fields');
      return;
    }

    console.log('New transaction:', { amount, description, category });
    
    // Reset form
    setAmount('');
    setDescription('');
    setCategory('revenue');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
        placeholder="Amount"
      />
      <input 
        type="text"
        value={description}
        onChange={(e) => setDescription(e.target.value)}
        placeholder="Description"
      />
      <button type="submit">Add Transaction</button>
    </form>
  );
}
DataFlow Dashboard

What just happened?

Form submission prevents default browser behavior, validates data from state, processes it, and resets the form. No DOM queries needed - React already has all the data. Try submitting with empty fields to see validation.

Controlled forms give you complete control over the submission process. Validation happens against state variables, not DOM elements. Resetting the form means resetting state variables. The form inputs automatically update to reflect the new state.

When to Use Controlled Components

Use controlled components when you need validation, formatting, conditional logic, or multiple inputs working together. For simple forms that just collect data on submit, uncontrolled components might be simpler. But controlled components give you the most power and predictability.

The power of controlled components becomes obvious in complex forms. React can validate fields as users type, disable submit buttons until forms are valid, transform input values, and sync multiple related fields. This control comes at the cost of more code but delivers better user experience. Modern React applications use controlled components by default. They align with React's philosophy of predictable data flow and make testing easier since form state lives in your component, not hidden in DOM elements.

Quiz

1. The DataFlow team needs to understand controlled components. What makes an input "controlled" in React?


2. You're building a settings panel with a checkbox for notifications. If notifications is a boolean state variable, what prop makes the checkbox controlled?


3. What's the main advantage of using controlled components for form submission in DataFlow?


useEffect Hook

Handle side effects like API calls, timers, and subscriptions with React's most versatile hook.