useReducer — the dispatch model

[check: …]
📖 guide (.md) ← react deep dive
📖 Pair this live React playground with the companion guide (.md) — this page is the rendered ground truth. ↗ Builds on frontend/react: useState (the single-setState model this evolves).

useState → useReducer: the evolution

useState gives you one value and one setter. When state logic grows — multiple fields, conditional updates, action-driven transitions — N independent setters become spaghetti. useReducer centralizes all transitions in ONE pure function: reducer(state, action) → newState. You call dispatch({{type:'ADD', text:'Learn React'}}) and the reducer decides what happens.

pieceroleanalogy
reducer pure function: (state, action) → newState the gearbox — receives a gear shift, outputs new speed
dispatch sends an action to the reducer the gear shift — you don't change gears directly, you request a shift
action plain object: {{type, ...payload}} the shift instruction — "shift up", "shift down"
initialState the starting state neutral gear — where you begin

1 · the reducer you write (edit me)

2 · Babel compiles JSX → element tree

<TodoApp/> becomes React.createElement(TodoApp). Each dispatch() calls the reducer, React gets the new state, re-renders.

// (hit "compile & render" to see Babel's output)

3 · live React (the dispatch model, proven)

Click "Add" to dispatch {'{type:"ADD"}'}, click a todo to dispatch {'{type:"TOGGLE"}'}, click ✕ to dispatch {'{type:"DELETE"}'}. The gold-check does this automatically: 3× Add, toggle item 1, delete item 1 → asserts the reducer handled every action correctly.

items: · gold: 3× Add → toggle → delete → expect 2 items, 0 done

intent → pattern

intentpatternwhy
add an item case 'ADD': return [...state, newItem] immutable spread — never .push()
update one item state.map(t => t.id === id ? {...t, done: !t.done} : t) map returns a new array; spread copies the matched item
remove an item state.filter(t => t.id !== id) filter returns a new array without the removed item
reset everything case 'CLEAR': return [] or remount with a new key to wipe all state
multi-field state {count:0, step:1, status:'idle'} + action-driven transitions one reducer replaces N setters — all transitions in one place