useReducer Hook
0/3 in this phase0/36 across the roadmap
📖 Concept
useReducer is an alternative to useState that is better suited for complex state logic involving multiple sub-values or when the next state depends on the previous one.
The Reducer Pattern:
- State: The current data.
- Action: An object describing what happened (e.g.,
{ type: 'INCREMENT' }). - Reducer: A pure function that takes the current state and an action, and returns the new state.
- Dispatch: A function used to send actions to the reducer.
When to use useReducer:
- You have complex state (objects with many fields).
- One state update depends on another state value.
- You want to separate state logic from the component's UI code.
- You want to make testing state transitions easier.
💻 Code Example
code
1import React, { useReducer } from 'react';23// 1. Initial State4const initialState = { count: 0, step: 1 };56// 2. Reducer Function (Pure, no side effects!)7function reducer(state, action) {8 switch (action.type) {9 case 'increment':10 return { ...state, count: state.count + state.step };11 case 'decrement':12 return { ...state, count: state.count - state.step };13 case 'setStep':14 return { ...state, step: action.payload };15 case 'reset':16 return initialState;17 default:18 throw new Error('Unknown action type');19 }20}2122function AdvancedCounter() {23 // 3. Initialize useReducer24 const [state, dispatch] = useReducer(reducer, initialState);2526 return (27 <div className="p-8 max-w-sm mx-auto bg-white shadow-xl rounded-2xl">28 <h1 className="text-2xl font-bold mb-4">Reducer Counter</h1>2930 <div className="text-5xl font-mono text-center mb-8">{state.count}</div>3132 <div className="flex justify-center space-x-4 mb-6">33 <button34 onClick={() => dispatch({ type: 'decrement' })}35 className="w-12 h-12 rounded-full bg-red-100 text-red-600 text-2xl"36 >37 -38 </button>39 <button40 onClick={() => dispatch({ type: 'increment' })}41 className="w-12 h-12 rounded-full bg-green-100 text-green-600 text-2xl"42 >43 +44 </button>45 </div>4647 <div className="space-y-2">48 <label className="text-sm text-gray-500 block">Step Size: {state.step}</label>49 <input50 type="range" min="1" max="10"51 value={state.step}52 onChange={(e) => dispatch({ type: 'setStep', payload: Number(e.target.value) })}53 className="w-full"54 />55 </div>5657 <button58 onClick={() => dispatch({ type: 'reset' })}59 className="mt-6 w-full py-2 text-gray-400 hover:text-gray-600 text-sm underline"60 >61 Reset to Defaults62 </button>63 </div>64 );65}6667export default AdvancedCounter;
🏋️ Practice Exercise
- Rewrite a complex
useStateform (like the one in the previous topic) usinguseReducer. - Build a 'Shopping Cart' reducer that handles 'ADD_ITEM', 'REMOVE_ITEM', and 'UPDATE_QUANTITY'.
- Create a 'Timer' component where
useReducerhandles 'START', 'STOP', 'TICK', and 'RESET'. - Practice moving your reducer function outside the component file to see how it can be tested independently.
⚠️ Common Mistakes
Mutating state inside the reducer (Reducers MUST be pure).
Forgetting to return the state in the 'default' case (this can cause the state to become undefined).
Using
useReducerfor very simple state that only needs a singleuseState.Performing side effects (like API calls) inside a reducer.
💼 Interview Questions
🎤 Mock Interview
Practice a live interview for useReducer Hook
Was this topic helpful?