One Object of State for Many Inputs
Controlled Forms
For forms with multiple inputs, hold values in a single object keyed by the input's `name`.
What you'll learn
- Wire many inputs to one piece of state
- Submit a form and prevent the default
- Reset and validate at form level
For a single input, one useState is fine. For a form with five
fields, you don’t want five useStates. Keep one object keyed by
field name.
The Pattern
function SignupForm() {
const [form, setForm] = useState({ name: "", email: "", age: "" });
function update(e) {
const { name, value } = e.target;
setForm(prev => ({ ...prev, [name]: value }));
}
function handleSubmit(e) {
e.preventDefault();
console.log("submit", form);
}
return (
<form onSubmit={handleSubmit}>
<input name="name" value={form.name} onChange={update} />
<input name="email" value={form.email} onChange={update} type="email" />
<input name="age" value={form.age} onChange={update} type="number" />
<button>Sign up</button>
</form>
);
} One onChange for every input. The name attribute on each input
tells update which field to set.
Why name on the Input?
Two reasons:
- The
e.target.namelets one handler service every field - If the form ever falls back to a native submit (e.g., JS off), the field names are what the browser sends
Submit Logic
onSubmit on the <form> fires when the user presses Enter inside
an input or clicks a <button> (not <button type="button">).
e.preventDefault() keeps the browser from navigating, so you can
take over.
function handleSubmit(e) {
e.preventDefault();
if (!form.name.trim()) {
setError("Name required");
return;
}
send(form);
} Reset
Set the state back to the initial object:
const initial = { name: "", email: "", age: "" };
const [form, setForm] = useState(initial);
function reset() {
setForm(initial);
} Validating
Validate any time — on submit, on blur, or live as the user types. Derived from state, no extra storage:
const errors = {
name: form.name.trim() ? "" : "Required",
email: form.email.includes("@") ? "" : "Invalid email",
};
const isValid = !errors.name && !errors.email;
return (
<form onSubmit={handleSubmit}>
{/* ... inputs ... */}
<button disabled={!isValid}>Sign up</button>
</form>
); When To Reach For a Library
For long forms, async validation, or complex schemas, react-hook-form is what most teams use. The vanilla pattern above is plenty for short forms (which most are).
Up Next
A reminder about updating arrays and objects in state — and the mistake that breaks everything.
Immutable Updates →