Learn to drive every form field from React state so you can validate and submit reliable data.
Open this lesson in KodokonIn React, a form becomes predictable when every field is controlled: the displayed value comes from the component's state, and every keystroke goes through onChange to update that state. The DOM is no longer the source of truth - your state is. In practice, you bind the value prop to a state variable and provide a handler that calls the update function.
import { useState } from "react";
function EmailField() {
const [email, setEmail] = useState("");
return (
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
);
}A real form rarely has a single field. Rather than stacking up useState calls, group the values into one object and write a generic handler. Each field's name attribute must match a key of the object exactly: that is what tells the handler which property to update.
const [form, setForm] = useState({
username: "",
password: "",
});
function handleChange(e) {
const { name, value } = e.target;
setForm((prev) => ({ ...prev, [name]: value }));
}That leaves submission. By default, the browser reloads the page when a form is submitted: call e.preventDefault() at the top of your onSubmit handler. It is also the right place to validate the data before sending it, since the entire form state is already at hand.
function handleSubmit(e) {
e.preventDefault();
if (form.username.trim() === "") {
alert("Username is required.");
return;
}
console.log("Submitting:", form);
}
return (
<form onSubmit={handleSubmit}>
<input
name="username"
value={form.username}
onChange={handleChange}
/>
<button type="submit">Create account</button>
</form>
);value={email} to an <input> without providing an onChange. What happens?e.preventDefault() for in the onSubmit handler?