Complex State with useReducer
How to manage complex, interconnected state by moving the update logic into a single centralized function.
What is a Reducer?
useReducer is an alternative to useState. While useState is great for simple independent values (like a boolean or a string), useReducer is designed for state objects that have multiple properties that update together.
Instead of telling React *what* the new state should be (e.g., setTasks([...tasks, newTask])), you tell React *what the user just did* by 'dispatching an action' (e.g., dispatch({ type: 'added_task', text: 'Buy milk' })).
A separate function—the 'reducer'—takes that action, applies the logic, and calculates the new state.
The Spaghettification Problem
Imagine a complex Task Manager component. It needs tasks (array), isEditing (boolean), editingId (number), and draftText (string).
If a user clicks 'Edit', you have to call setIsEditing(true), setEditingId(task.id), and setDraftText(task.text) all at once.
Your event handlers become bloated with state-updating logic. It's easy to forget one setState call and introduce bugs where the UI is in an invalid state (like isEditing being true, but editingId being null).
The Three Parts of useReducer
1. **The State:** The data itself.
2. **The Action:** A simple object describing what happened (usually with a type string).
3. **The Reducer Function:** A pure JavaScript function that takes (currentState, action) and returns the newState using a switch statement.
Because the reducer is a pure function, you can actually move it completely outside of your component, making it incredibly easy to test independently.
The Reducer Pipeline
Interact with the complex state widget below. Watch how UI interactions create 'Action' objects that flow into the centralized Reducer function to calculate the new state tree.
Task Manager
taskReducer(state, action)
{
"tasks": [],
"nextId": 1
}Notice how the UI components don't update state directly. They just 'dispatch an action' (the flying blue object). The Reducer function receives this action, evaluates it, and calculates the next state.
Check yourself
Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.
Remember this
useReducercentralizes complex state logic into a single function.- Components 'dispatch actions' instead of calling
setStatedirectly. - The reducer is a pure function:
(state, action) => newState. - Reducers must NEVER mutate the current state directly.
- Reducers must NEVER contain side effects (like fetching data).
Done with this concept?
Mark it complete to track your progress. No login needed.