function reducer(state, action) {
switch (action.type) {
case 'added':
return [...state, { id: action.id, text: action.text }];
case 'deleted':
return state.filter(t => t.id !== action.id);
default:
throw new Error('Unknown action: ' + action.type);
}
}
const [todos, dispatch] = useReducer(reducer, []);
Choose useReducer when state has several related parts or many ways to change. It moves update logic into a pure function, (state, action) => newState, called a reducer. Components dispatch actions that describe what happened, like { type: 'added', text }. The reducer decides how the state changes.
It is worth it when:
- state has several related fields that update together
- the next state depends on the previous one in non-trivial ways
- many event handlers modify the same state
Keeping all the changes in one place makes them easier to read, test alone and debug. For example, you can log every action.
useState is simpler for independent values, like a toggle or an input string. Both trigger re-renders the same way, and React does not prefer one for performance. dispatch has a stable identity: it is the same function on every render. That makes it easy to pass down through context together with the state.
- A reducer is a pure function
(state, action) => newState. useReduceris good for complex or related state with many update paths.- The update logic sits in one place and is easy to unit test.
useStateis simpler for independent values.dispatchis stable, so it works well with context.