⚛️
EPISODE 04
Reducer pattern · dispatch · actions · when over useState
useReducer
Handle complex state with useReducer — a tiny reducer function and dispatched actions. Predictable, testable, and great for multi-step state.
useReducerdispatchactionsRedux pattern
Duration
⏱ About 2 hours
Level
📊 Intermediate
Prerequisite
🎯 react-02
OUTCOME
A to-do list managed by a typed reducer with action types
What you'll learn
- 1Write a reducer function: (state, action) => newState
- 2Dispatch actions instead of calling many setters
- 3Use a discriminated-union Action type for type safety
- 4Choose useReducer over useState for complex, interrelated state
1. Reducer Shape
tsx
type State = { count: number; step: number };
type Action =
| { type: "increment" }
| { type: "decrement" }
| { type: "set-step"; 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 "set-step": return { ...state, step: action.step };
case "reset": return { count: 0, step: 1 };
}
}2. Use the Reducer
tsx
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 });
return (
<div>
<p>{state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<input type="number" value={state.step}
onChange={e => dispatch({ type: "set-step", step: +e.target.value })} />
<button onClick={() => dispatch({ type: "reset" })}>reset</button>
</div>
);
}3. useState vs useReducer
| Pick | When |
|---|---|
| useState | 1–2 simple values, updates are independent |
| useReducer | Multiple values that change together, complex update logic, you want to test the reducer in isolation |
Example code / lecture materials
All lecture materials and example code are openly available on GitHub.
View on GitHub ↗