useReducer Hook
Complex state logic with reducers in React
What you'll learn
Replace useState with useReducer for complex state logic
Define reducer functions with explicit action types
Dispatch actions to trigger state transitions
Type reducer actions with discriminated unions in TypeScript
Extract reducer logic for testability outside the component
Understand when useReducer is better than useState
useReducer is an alternative to useState for state logic that involves multiple sub-values or depends on previous state.
Basic Pattern
"use client"
import { useReducer } from "react"
type State = { count: number }
type Action = { type: "increment" } | { type: "decrement" } | { type: "reset" }
function reducer(state: State, action: Action): State {
switch (action.type) {
case "increment":
return { count: state.count + 1 }
case "decrement":
return { count: state.count - 1 }
case "reset":
return { count: 0 }
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 })
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
</div>
)
}
When to Use
Use useReducer when:
- State has complex update logic
- Multiple fields update together
- Next state depends heavily on previous state
For simple values, stick with useState.
Next Steps
Key Takeaways
useReducer takes a reducer function (state, action) => newState and an initial state value
Reducer actions are typically objects with a type property and optional payload for additional data
Dispatch sends actions to the reducer — the component never calls the reducer directly
TypeScript discriminated unions type-check action types and payloads together for safety
Reducer logic is pure and testable outside React — the same input always produces the same output state