What is the useReducer hook in React?
ReactThe short answer
useReducer is an alternative to useState for managing state in React. Instead of updating state directly with a setter function, you dispatch actions that describe what happened, and a reducer function decides how the state should change. It is useful when you have complex state logic or when the next state depends on the previous state.
How it works
import { useReducer } from 'react';function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; case 'reset': return { count: 0 }; default: return state; }}function Counter() { const [state, dispatch] = useReducer(reducer, { count: 0, }); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })} > + </button> <button onClick={() => dispatch({ type: 'decrement' })} > - </button> <button onClick={() => dispatch({ type: 'reset' })}> Reset </button> </div> );}Here is what is happening:
- The reducer function takes the current state and an action, and returns the new state
- dispatch sends an action to the reducer
- When you click "+", it dispatches
{ type: 'increment' }. The reducer sees this action and returns a new state withcount + 1
The reducer is a pure function — it takes input and returns output without side effects.
When to use useReducer vs useState
useState is better when:
- State is simple (a string, number, boolean)
- State updates are independent from each other
- You have one or two pieces of state
useReducer is better when:
- You have multiple related pieces of state that change together
- The next state depends on the previous state
- State update logic is complex
Here is an example where useReducer makes more sense:
function reducer(state, action) { switch (action.type) { case 'fetch_start': return { ...state, loading: true, error: null }; case 'fetch_success': return { ...state, loading: false, data: action.payload, }; case 'fetch_error': return { ...state, loading: false, error: action.payload, }; default: return state; }}function UserProfile({ userId }) { const [state, dispatch] = useReducer(reducer, { data: null, loading: false, error: null, }); useEffect(() => { dispatch({ type: 'fetch_start' }); fetch(`/api/users/${userId}`) .then((res) => res.json()) .then((data) => dispatch({ type: 'fetch_success', payload: data }) ) .catch((err) => dispatch({ type: 'fetch_error', payload: err.message, }) ); }, [userId]); if (state.loading) return <p>Loading...</p>; if (state.error) return <p>Error: {state.error}</p>; if (!state.data) return null; return <h1>{state.data.name}</h1>;}With useState, you would need three separate state variables and make sure they stay in sync. With useReducer, all state transitions are in one place and every action updates the state consistently.
Actions with payload
Actions can carry data using a payload property:
function reducer(state, action) { switch (action.type) { case 'add_todo': return [ ...state, { id: Date.now(), text: action.payload, done: false, }, ]; case 'toggle_todo': return state.map((todo) => todo.id === action.payload ? { ...todo, done: !todo.done } : todo ); case 'delete_todo': return state.filter( (todo) => todo.id !== action.payload ); default: return state; }}The type tells the reducer what to do, and the payload gives it the data it needs.
Common Pitfalls
A common mistake is reaching for useReducer when useState would be simpler. If you just need to toggle a boolean or update a single value, useState is the right choice. useReducer adds boilerplate (the reducer function, action types) that is not worth it for simple state. Use it when the state logic is actually complex enough to benefit from centralization.
Interview Tip
When explaining useReducer, compare it to useState with a concrete example. The data fetching example (loading/success/error states) is perfect because it shows why managing three related state values with useState is messy. Also mention that useReducer is inspired by Redux — this shows the interviewer you understand the broader state management ecosystem.
Why interviewers ask this
This question tests if you know when to use different state management approaches in React. Interviewers want to see if you can pick the right tool for the job — useState for simple state, useReducer for complex state. A candidate who can explain the tradeoffs and give a practical example shows they have built real applications with thoughtful state management.