Events & Forms
Event Handlers
function Toolbar() {
function handleSave() {
console.log("saving…");
}
return (
<>
<button onClick={handleSave}>Save</button>
<button onClick={() => console.log("inline")}>Log</button>
</>
);
}Pass the function, don’t call it: onClick={handleSave} — with parentheses it would run on every render. To pass arguments, wrap in an arrow: onClick={() => remove(id)}.
Handlers receive the event object, same API as vanilla JS:
<input onKeyDown={(e) => e.key === "Enter" && submit()} />
<form onSubmit={(e) => { e.preventDefault(); save(); }}>Controlled Inputs
In React, the input’s value lives in state — the input displays state, and typing updates state. One source of truth:
function SearchBox() {
const [query, setQuery] = useState("");
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search courses…"
/>
);
}Now the React state always knows what’s in the box — you can validate live, clear it programmatically (setQuery("")), or filter a list as the user types.
A Complete Form
function SignupForm({ onSubmit }) {
const [form, setForm] = useState({ name: "", email: "", track: "django" });
const [agreed, setAgreed] = useState(false);
const [error, setError] = useState(null);
function update(e) {
const { name, value } = e.target;
setForm({ ...form, [name]: value }); // one handler for all text fields
}
function handleSubmit(e) {
e.preventDefault();
if (!form.email.includes("@")) {
setError("Enter a valid email");
return;
}
setError(null);
onSubmit(form);
}
return (
<form onSubmit={handleSubmit}>
<input name="name" value={form.name} onChange={update} required />
<input name="email" type="email" value={form.email} onChange={update} />
<select name="track" value={form.track} onChange={update}>
<option value="django">Django</option>
<option value="react">React</option>
</select>
<label>
<input
type="checkbox"
checked={agreed}
onChange={(e) => setAgreed(e.target.checked)}
/>
I agree to the terms
</label>
{error && <p className="error">{error}</p>}
<button disabled={!agreed}>Sign up</button>
</form>
);
}Patterns to note: the [name]: value computed key handles any number of text fields with one function; checkboxes use checked + e.target.checked; the button disables itself from state; errors are just state rendered conditionally.
Why Controlled Beats Uncontrolled
You can read inputs at submit time with refs/FormData (“uncontrolled”), and it’s fine for simple forms. Controlled inputs earn their keep the moment you need live validation, dependent fields, character counters, or instant filtering — which is most real forms. Libraries like react-hook-form optimise big forms, but they assume you understand this foundation.