What is the useReducer hook in React?

React

The 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:

  1. The reducer function takes the current state and an action, and returns the new state
  2. dispatch sends an action to the reducer
  3. When you click "+", it dispatches { type: 'increment' }. The reducer sees this action and returns a new state with count + 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.