useReducer
React · Reference cheat sheet
useReducer
React · Reference cheat sheet
📋 Overview
useReducer(reducer, initialState) manages state with a (state, action) => nextState function. Prefer when updates are complex, multi-field, or easier to test as pure transitions than scattered setState calls.
🔧 Core concepts
- Reducer — pure; no side effects.
- Dispatch — stable identity;
dispatch(action). - Init —
useReducer(reducer, initArg, init)for lazy setup. - vs useState — state for simple values; reducer for structured transitions.
- Actions — discriminated unions in TypeScript.
💡 Examples
import { useReducer } from "react";
type State = { count: number; step: number };
type Action =
| { type: "increment" }
| { type: "decrement" }
| { type: "setStep"; step: number }
| { type: "reset" };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "increment":
return { ...state, count: state.count + state.step };
case "decrement":
return { ...state, count: state.count - state.step };
case "setStep":
return { ...state, step: action.step };
case "reset":
return { count: 0, step: 1 };
default:
return state;
}
}
export function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 });
return (
<div>
<p>{state.count}</p>
<button type="button" onClick={() => dispatch({ type: "increment" })}>
+
</button>
<button type="button" onClick={() => dispatch({ type: "setStep", step: 5 })}>
step 5
</button>
</div>
);
}⚠️ Pitfalls
- Side effects inside reducers (fetch, DOM)—put in effects or event handlers.
- Mutating
stateinstead of returning a new object. - Overusing for a single boolean toggle.
- Forgetting exhaustive
switchhandling in TS.
🔗 Related
- useState.md — simpler state
- hooks.md — rules
- context.md — dispatch via context
- testing.md — pure reducer tests
- performance.md — update patterns