Forms and Controlled Inputs
Controlled versus uncontrolled inputs, and validating what the user typed.
What you'll learn
- What makes an input controlled, and why you would want that
- Handling many fields without writing many handlers
- Checkboxes, selects, and textareas
- Validating, and submitting without reloading the page
Controlled inputs
An input is controlled when React supplies its value and an onChange
handler puts every keystroke back into state. State is the single source of
truth; the DOM only displays it.
That round trip is what lets you react to typing — transform it, validate it, or disable a button:
Typing lowercase produces uppercase, because what you see is state, not what you typed. The button enables itself at three characters.
A value without an onChange makes the input read-only, which is a common
first surprise. If you want a starting value the user can then edit freely, use
defaultValue — that is an uncontrolled input, where the DOM keeps the value
and you read it only when you need it.
Prefer controlled inputs. Reach for uncontrolled ones for simple fields where
nothing needs to react to each keystroke, or for <input type="file" />, which
is always uncontrolled.
Many fields, one handler
A handler per field does not scale. Keep the fields in one object and use the
input's name to decide which key to update:
Note the updater form, setForm((current) => ...), and the copy. Both are the
rules from useState: never mutate the object, and derive the next
value from the previous one.
The other input types
Three of them do not use value the way a text input does:
- Checkbox — uses
checked, and readsevent.target.checked - Select —
valuegoes on the<select>, not on an<option> - Textarea — takes
valueas a prop, not as its children
Submitting and validating
Put onSubmit on the <form> so the Enter key works, and call
preventDefault so the browser does not reload the page — the point from
Handling Events.
Validation is then ordinary JavaScript over your state. Showing errors only after a field has been touched keeps the form from shouting at someone who has not typed anything yet:
error is derived during render rather than stored in state — one fewer value
that can fall out of step.
Common mistakes
valuewith noonChange. The input becomes read-only.value={null}orundefined. React treats the input as uncontrolled and warns when a value arrives later. Start with"".- A handler per field. Use the
nameattribute and one handler. - Validation in state. Derive it from the field values instead.
Exercise
Three bugs: the input is read-only, the password field has its own redundant handler, and submitting reloads the page so nothing is ever logged. Fix all three — the form should show the submitted values.