Chapter 3 of 4

Controlled Inputs

Binding form fields to state so React is the single source of truth.

A controlled input takes its value from state and reports changes back through onChange. React holds the truth and the DOM just displays it.

function NameField() {
  const [name, setName] = useState("");

  return (
    <input
      value={name}
      onChange={(event) => setName(event.target.value)}
    />
  );
}

Because the value lives in state, you can validate it, transform it, or reset it from anywhere.

onChange={(event) => setName(event.target.value.toUpperCase())}

The different field types

// Text, email, number, textarea: use value
<input value={email} onChange={(e) => setEmail(e.target.value)} />
<textarea value={bio} onChange={(e) => setBio(e.target.value)} />

// Checkbox: use checked, and read event.target.checked
<input
  type="checkbox"
  checked={acceptsTerms}
  onChange={(e) => setAcceptsTerms(e.target.checked)}
/>

// Select: value goes on the select, not the option
<select value={country} onChange={(e) => setCountry(e.target.value)}>
  <option value="uk">United Kingdom</option>
  <option value="de">Germany</option>
</select>

One state object for a whole form

Rather than a useState per field, many forms keep one object and a shared change handler. The name attribute picks the field to update.

const [form, setForm] = useState({ name: "", email: "" });

function handleChange(event) {
  const { name, value } = event.target;
  setForm((previous) => ({ ...previous, [name]: value }));
}

<input name="name" value={form.name} onChange={handleChange} />
<input name="email" value={form.email} onChange={handleChange} />