useReducer & Complex State Transitions
useReducer is useful when a component has many related state transitions. Instead of exposing many setters, you describe events as actions and centralize state transitions in a reducer.
A reducer should be pure: given the same state and action, it returns the same next state. Side effects such as network requests belong outside the reducer.
Reducers become especially valuable when transitions have domain meaning: cart/itemAdded, checkout/submitted, or editor/saved. This produces a more explicit state model than a collection of loosely related booleans.
Reducer pattern
type State = { count: number };
type Action =
| { type: "increment" }
| { type: "decrement" };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "increment": return { count: state.count + 1 };
case "decrement": return { count: state.count - 1 };
}
}💡 The reducer centralizes allowed state transitions while the component dispatches semantic actions.
Key Takeaways
- Reducers model transitions, not side effects.
- Use actions that describe what happened.
- Discriminated action types work especially well with TypeScript.