When state has multiple related fields and transitions (like a transaction form with amount, recipient, status, and error), `useReducer` centralizes the logic in one pure function.
type State = { amount: string; status: "idle" | "sending" | "sent" | "error"; error?: string };
type Action = { type: "setAmount"; amount: string } | { type: "send" } | { type: "sent" } | { type: "fail"; error: string };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "setAmount": return { ...state, amount: action.amount };
case "send": return { ...state, status: "sending" };
case "sent": return { ...state, status: "sent" };
case "fail": return { ...state, status: "error", error: action.error };
}
}Keeping the reducer pure (no side effects, no randomness) makes it trivially testable in isolation, without rendering any component.