useReducer Hook
useReducer manages state with a reducer function (state, action) => newState and a dispatch function. It excels when state updates follow predictable action types or involve nested structures.
Reducer Pattern in Components const [state, dispatch] = useReducer(reducer, initialState) centralizes update logic in one pure function instead of scattered setState calls.
Similar to Redux reducers but local to one component (or shared via context). Good for forms, wizards, and UI state machines.
function reducer(state, action) {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'reset': return { count: 0 };
default: return state;
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 }); Dispatch { type: 'increment' } to trigger predictable transitions.
useReducer API useReducer(reducer, initialState)
useReducer(reducer, initialArg, initFn) // lazy init
dispatch({ type: 'ACTION', payload: data }) Reducer must be pure — no side effects inside. Return new state objects; never mutate previous state. Action types are conventions — use strings or constants. Lazy init third argument computes initial state once. useState vs useReducer Choose the right local state tool.
Factor useState useReducer Update complexity Simple Multiple interrelated updates Logic location Inline in handlers Central reducer Testing Test handlers Test reducer in isolation Next step Context + reducer Redux / Zustand
Testing Reducers Reducers are pure functions — easy to unit test without rendering components.
expect(reducer({ count: 1 }, { type: 'increment' })).toEqual({ count: 2 }); Simplifying with Immer The use-immer hook or Immer in reducers lets you write mutating-looking code that produces immutable updates — helpful for deep nested state.
Common Mistakes Mutating state inside the reducer. Using useReducer for a single boolean toggle. Putting async logic inside reducers. Over-engineering simple forms with action types. Key Takeaways useReducer centralizes complex state update logic. Reducers are pure (state, action) => newState functions. Easier to test than scattered setState calls. Foundation for Context + reducer and Redux patterns.
Pro Tip
If you describe state changes as verbs (ADD_TODO, TOGGLE, CLEAR), useReducer is probably a good fit.
useCallback Hook Go to next item