050
LVL 02 — CIRCUIT BREAKER SESSION 050 DAY 50

USE REDUCER

🎫 PIXELCRAFT-038
🐛 Bug / 🔧 Refactor | 🟠 Hard | Priority: 🔴 Critical

The editor has 15+ useState calls scattered across components. Undo sometimes restores partial state. Filter values desync from the canvas. State transitions are unpredictable. We need centralized, predictable state management.
CONCEPTS.UNLOCKED
🎛
useReducer
Centralized state management with explicit actions. All state in one object, all transitions in one function. Replace 15+ scattered useState calls with one predictable system.
📨
Actions
{ type: 'SET_FILTER', payload: { name: 'brightness', value: 20 } } — a description of what happened. Actions are data, not code. Every state change is described by an action.
Reducer Function
(state, action) => newState — pure, predictable, testable. Given the same state and action, always returns the same result. No side effects, no surprises.
🎯
dispatch()
Trigger state transitions from any component. dispatch({ type: 'UNDO' }) — the component says WHAT happened, the reducer decides HOW state changes.
🔀
State Machines
Explicit states and defined transitions. IDLE → FILTERING → PREVIEW → APPLIED. Each dispatch is a state transition. No impossible state combinations.
📸
Atomic State Snapshots
The reducer captures complete state atomically. Undo restores the entire filter state at once — no partial updates, no desync. This fixes the undo bug.
HANDS-ON.TASKS
01
Design the State Shape
const initialState = { image: null, filters: { brightness: 0, contrast: 1, saturation: 0, grayscale: false, invert: false, }, layers: [], activeTool: 'select', undoStack: [], redoStack: [], zoom: 1, panOffset: { x: 0, y: 0 }, isProcessing: false, error: null, };
One object holds the entire editor state. Every piece of data the editor needs — image, filters, layers, tools, undo history, zoom, errors — all in one place. Single source of truth.
02
Write the Reducer
function editorReducer(state, action) { switch (action.type) { case 'LOAD_IMAGE': return { ...state, image: action.payload, filters: { ...initialState.filters }, undoStack: [], redoStack: [], }; case 'SET_FILTER': return { ...state, undoStack: [ ...state.undoStack, state.filters ], redoStack: [], filters: { ...state.filters, [action.payload.name]: action.payload.value }, }; case 'UNDO': { if (state.undoStack.length === 0) return state; const previous = state.undoStack[ state.undoStack.length - 1 ]; return { ...state, filters: previous, undoStack: state.undoStack.slice(0, -1), redoStack: [ ...state.redoStack, state.filters ], }; } case 'REDO': { if (state.redoStack.length === 0) return state; const next = state.redoStack[ state.redoStack.length - 1 ]; return { ...state, filters: next, redoStack: state.redoStack.slice(0, -1), undoStack: [ ...state.undoStack, state.filters ], }; } case 'SELECT_TOOL': return { ...state, activeTool: action.payload }; case 'SET_ZOOM': return { ...state, zoom: action.payload }; case 'SET_PROCESSING': return { ...state, isProcessing: action.payload }; case 'SET_ERROR': return { ...state, error: action.payload }; case 'RESET_ALL': return { ...initialState, image: state.image }; default: throw new Error( `Unknown action: ${action.type}` ); } }
Every case is pure: takes state + action, returns new state. No mutations, no side effects, no API calls. SET_FILTER automatically pushes to undoStack and clears redoStack. UNDO/REDO are symmetric operations.
03
Use in the App
function App() { const [state, dispatch] = useReducer( editorReducer, initialState ); return ( <EditorContext.Provider value={{ state, dispatch }}> <Toolbar activeTool={state.activeTool} onToolSelect={(tool) => dispatch({ type: 'SELECT_TOOL', payload: tool })} /> <CanvasArea image={state.image} filters={state.filters} zoom={state.zoom} /> <SettingsPanel filters={state.filters} onFilterChange={(name, value) => dispatch({ type: 'SET_FILTER', payload: { name, value } }) } /> </EditorContext.Provider> ); }
04
Verify the Undo Bug is Fixed

The reducer captures complete filter state atomically. No more partial undo restores.

Test: rapid slider movements, undo/redo sequences, concurrent actions — all transitions predictable.

05
Close the Ticket
git switch -c refactor/PIXELCRAFT-038-reducer git add src/ git commit -m "Centralize state with useReducer, fix undo bug (PIXELCRAFT-038)" git push origin refactor/PIXELCRAFT-038-reducer # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

State machines and finite automata.

Each dispatch is a state transition. The reducer makes transitions explicit, predictable, and testable.

// Formal state machines are used in:

TCP protocol     → LISTEN → ESTABLISHED
                   → CLOSE_WAIT
Game AI          → patrol → chase →
                   attack → flee
Hardware         → flip-flops, registers
Compiler theory  → regex ARE finite automata
UI logic         → IDLE → FILTERING →
                   PREVIEW → APPLIED

// Properties you desperately need
// in complex applications.
"Reducer Mastery"
[A] Write unit tests for the reducer: test each action type with specific state/action inputs and verify the exact output. Pure functions are trivially testable — prove it.
[B] Add a "time travel" debugger: log every action + resulting state to an array. Build a UI that lets you click any past state to jump to it. This is exactly what Redux DevTools does.
[C] Research XState — a full state machine library for JavaScript. Model the editor as a formal state chart with explicit states, transitions, and guards. Compare with the reducer approach.
REF.MATERIAL
ARTICLE
React Team
Official guide: when to switch from useState to useReducer, writing reducers, dispatch patterns, and comparison with useState.
useReducerOFFICIALESSENTIAL
VIDEO
Web Dev Simplified
Practical useReducer walkthrough: converting from useState, action design, and when useReducer beats useState.
useReducerPRACTICAL
ARTICLE
React Team
Complete API reference: signature, dispatch, lazy initialization, and Bailing out of dispatches.
useReducerAPIREFERENCE
ARTICLE
Wikipedia
Theory: states, transitions, inputs, outputs. How state machines model computation, protocols, and UI logic.
STATE MACHINESTHEORYCS
VIDEO
Fireship
Overview: useState vs useReducer vs Context vs Redux. When each approach makes sense and how they compose.
STATEOVERVIEW
// LEAVE EXCITED BECAUSE
All editor state lives in one place. Every transition is explicit. The undo bug is fixed because state snapshots are atomic. The app is predictable and debuggable.