Skip to main content

useReducer() vs useState()

The main difference

HookWhat for
useStateSimple, independent state (a single value, short logic)
useReducerComplex update logic, many interrelated pieces of data, a clear structure of "actions"

When it is worth using useReducer()

1. When the update logic is complex or multi-step

If you have several types of changes that depend on different conditions,
it makes more sense to have one centralized reducer instead of many setState calls and if statements.

Example:

javascript
const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 }); function reducer(state, action) { switch (action.type) { case "increment": return { ...state, count: state.count + state.step }; case "decrement": return { ...state, count: state.count - state.step }; case "setStep": return { ...state, step: action.payload }; default: return state; } }

Here useReducer is more convenient than three separate useState calls.

2. When states are interrelated

If changing one field affects others, useReducer lets you
manage them in one place, instead of in dozens of setState calls.

Example:

javascript
function formReducer(state, action) { switch (action.type) { case "CHANGE_FIELD": return { ...state, [action.field]: action.value }; case "RESET": return initialForm; default: return state; } }

Managing a form in one reducer is simpler and more reliable than 5 separate useState calls.

3. When you need predictable behavior and a "Redux approach"

If you want updates to be transparent, testable, and centralized
(for example, like in Redux), useReducer does this with React's built-in tools.

"The reducer makes state deterministic:
the same action → always the same new state."

4. When updates depend on the previous state

Instead of writing a bunch of setState(prev => ...) calls,
the reducer handles this for you - all the logic is already inside reducer().

5. When you want to use context for global state

useReducer is very often combined with useContext to create
global state without Redux / Zustand:

javascript
const StoreContext = createContext(); function StoreProvider({ children }) { const [state, dispatch] = useReducer(reducer, initialState); return <StoreContext.Provider value={{ state, dispatch }}>{children}</StoreContext.Provider>; }

This is a mini-Redux right inside React: clean, predictable, and with no external dependencies.

When you do NOT need useReducer

CaseBetter to use
Simple state (boolean, number, string)useState
Independent statesSeveral useState calls
Logic is short and obvioususeState is simpler and more readable
No different "action types"useReducer is redundant

Comparison example

With useState

javascript
const [count, setCount] = useState(0); const [step, setStep] = useState(1); const increment = () => setCount(prev => prev + step);

Simple and readable - ideal if the logic is simple.

With useReducer

javascript
const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 }); function reducer(state, action) { switch (action.type) { case "increment": return { ...state, count: state.count + state.step }; case "setStep": return { ...state, step: action.payload }; default: return state; } }

More convenient if the logic grows - for example, decrement, reset, double, and so on get added.

Summary

CriterionuseStateuseReducer
Simple logicYesNo
Complex interrelationsNoYes
Many types of changesNoYes
Centralized logicNoYes
Easy to writeYesa bit more code
Global state (via context)possibleideal

The main rule:

Use useState for simple states,
and useReducer when state-management logic becomes "mini business logic".

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.