useReducer — Complex State Logic
12 min read
When useState Isn't Enough
useReducer is a more structured alternative to useState for state that involves multiple sub-values or complex transitions. You dispatch actions to a reducer function that computes the next state.
jsx
import { useReducer } from 'react';
const initialState = { count: 0, step: 1 };
function reducer(state, action) {
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.payload };
case 'reset':
return initialState;
default:
throw new Error('Unknown action: ' + action.type);
}
}
function ConfigurableCounter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count} (step: {state.step})</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<input
type="number"
value={state.step}
onChange={e => dispatch({ type: 'setStep', payload: Number(e.target.value) })}
/>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}Prefer useReducer over useState when: you have 3+ state variables that change together, next state depends on previous in non-trivial ways, or you want to unit-test your state logic in isolation.
Ready to test yourself?
Practice React— quiz & coding exercises