Managing form state with useReducer keeps every field, the error message, and the reset behavior in one reducer instead of separate useState calls. One action updates a single field, and one reset action restores the whole form. This fits forms where several inputs move together, like a signup or settings screen.
One state object for the whole form
Hold the fields and the error message in a single object. That way one reducer owns everything the form shows, and a reset action can restore the entire shape at once.
const initial = { email: "", error: "" };The email field starts empty and the error starts empty. Keeping them together means one update can clear the error while changing the field. A reset action can then restore both in one return.
A reducer for change, error, and reset
The reducer handles three events: the user typed, validation failed, and the form was reset. Each case returns a fresh object.
function reducer(state, action) {
switch (action.type) {
case "changed": return { ...state, [action.field]: action.value, error: "" };
case "failed": return { ...state, error: "Enter a valid email." };
case "reset": return initial;
default: return state;
}
}The changed case copies the state, writes the new value into the given field, and clears any old error. The failed case keeps the email the user typed and adds the message, so the input is not wiped on a bad submit.
Render a controlled input
The input reads its value from the reducer state and dispatches a changed action on every keystroke. A label wraps the input, which gives it an accessible name.
function EmailForm() {
const [state, dispatch] = useReducer(reducer, initial);
return (
<form onSubmit={(e) => { e.preventDefault(); dispatch({ type: state.email.includes("@") ? "reset" : "failed" }); }}>
<label>Email <input value={state.email} onChange={(e) => dispatch({ type: "changed", field: "email", value: e.target.value })} /></label>
{state.error && <p role="alert">{state.error}</p>}
<button>Save</button>
</form>
);
}Typing updates email live through the changed action. Submitting checks the email and dispatches either reset or failed, and the alert paragraph announces the error to screen readers.
Add a field without touching the reducer
To add another field, extend the initial object and render one more labeled input. The changed action already writes to whatever field name it receives, so no case in the reducer needs to change. Dispatch changed with the new field name and the input value, and the field starts updating like the others.
Why the handler decides, not the reducer
Validation that reads the value and dispatches a result belongs in the submit handler. The reducer stays pure and only returns the next state, which keeps it testable and side effect free. Client checks are convenience only; the server still has to validate the real request.
For larger forms, see how to write actions and reducers for naming and purity rules. When validation, focus management, and performance become the bottleneck, a library such as React Hook Form earns its keep; how to build forms in React covers that path.
Rune AI
Key Insights
- Keep the whole form in one state object.
- One action updates a single field without losing others.
- Validate in the submit handler, not the reducer.
- Use a reset action to restore the form.
- Reach for a form library when validation grows.
Frequently Asked Questions
When should I use useReducer for a form instead of useState?
Is useReducer a replacement for React Hook Form?
Should I validate inside the reducer?
Conclusion
useReducer fits forms because fields, errors, and reset behavior all update together. Keep the whole form in one object, update one field per action, validate in the handler, and restore the form with a single reset action.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.