Skip to main content

useReducer() VS useState()

The main difference

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

When to use useReducer()

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

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

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 the pieces of state are interrelated

If changing one field affects others, useReducer lets you manage them in one place, instead of 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 with a single 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 (similar to Redux), useReducer provides this with React's built-in tools.

"A reducer makes state deterministic: the same action always produces the same new state."


4. When updates depend on the previous state

Instead of writing a pile of setState(prev => ...), 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 build 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 without external dependencies.


When you do NOT need useReducer

CaseBetter to use
Simple state (boolean, number, string)useState
Independent pieces of stateSeveral useState
The logic is short and obvioususeState is simpler and more readable
There are no different "action types"useReducer is overkill

Comparison example

With useState

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

Simple and readable - perfect when 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, if decrement, reset, double, and so on are added later.


Summary

CriterionuseStateuseReducer
Simple logicyesno
Complex interrelationsnoyes
Many types of changenoyes
Centralized logicnoyes
Ease of writingyesa bit more code
Global state (via context)possibleideal

Main rule:

Use useState for simple state, 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.