Forms
Challenge 1 from Chapter 4 already built one controlled input — an input whose displayed value comes from state, updated through onChange. This chapter generalizes that pattern across a whole form with several fields, plus the input types that don't use plain text (checkbox, radio, select).
Controlled vs Uncontrolled
An input with no value prop is uncontrolled — the browser's own DOM tracks what's typed, and React only finds out by reading it later. A controlled input ties its value directly to a piece of state, with onChange updating that state on every keystroke — React becomes the single source of truth for what's in the field, not the DOM. Controlled inputs are the standard approach in React, since the current value is always available in state wherever it's needed (validation, submitting, displaying it elsewhere on the page) without ever having to reach into the DOM to ask.
One Handler for Several Fields
Rather than one useState call per field, a form with several fields commonly uses a single state object, with one shared handleChange reading the field's own name attribute off e.target to know which key to update. [name]: value is a computed property name — plain JavaScript, not anything React-specific — letting one line update whichever key matches the input that fired the event. The ...prev spread keeps every other field's value unchanged, updating only the one key that changed.
setFormData({ [name]: value }) (without spreading prev first) would replace the entire state object with just the one changed field — every other field's value would be wiped out on the very next keystroke. Always spread the previous state first when updating just one key of an object, exactly as covered for state objects generally back in Chapter 3.
Checkboxes and the checked Prop
A checkbox doesn't use value to control its display state — it uses checked, paired with e.target.checked (a boolean) rather than e.target.value. Wrapping the input in a <label> lets clicking the surrounding text also toggle the checkbox, a free accessibility win that costs nothing extra to add.
Select Dropdowns
A <select> works the same as a text input — value and onChange on the <select> element itself (not the individual <option>s) — which is a small but genuine difference from plain HTML, where the selected attribute is normally set on the chosen <option> directly.
onSubmit handler (from Chapter 4) reads from the same state used to control the inputs — there's no need to separately collect values at submit time, since every keystroke has already kept state in sync. handleSubmit just calls event.preventDefault() and then does whatever needs to happen with the already-current formData.
| Element | Controlled via |
|---|---|
| <input type="text"> | value + e.target.value |
| <input type="checkbox"> | checked + e.target.checked |
| <select> | value on the select itself, same as text inputs |
| <textarea> | value + e.target.value, same as text inputs |
Coding Challenges
Build a form with two text inputs (firstName, lastName) sharing one formData state object and one handleChange function, using the name/computed-property-name pattern. Display "Hello, [firstName] [lastName]" live below the form.
📄 View solutionBuild a component with a checkbox controlled by boolean state, and a select dropdown with at least 3 options controlled by string state. Display both current values below the form.
📄 View solutionBuild a feedback form with a textarea (controlled) and a submit button. On submit, prevent the default behavior, log the message, and reset the textarea back to an empty string.
📄 View solutionChapter 7 Quick Reference
- Controlled input —
valuetied to state,onChangeupdates that state - One shared
handleChange+nameattribute + computed property name ([name]: value) handles several fields at once - Always spread the previous state (
...prev) when updating just one key of a form-data object - Checkboxes use
checked/e.target.checked, notvalue - Select and textarea are controlled the same way as text inputs
- A submit handler can read directly from state — no separate step needed to "collect" form values
- Next chapter: useEffect and side effects — running code in response to renders, not just events