All Posts
React

React State Management: useState vs useReducer vs Context vs Zustand

June 5, 2025

The React state management "debate" is mostly noise. In practice, the right tool depends entirely on how far the state needs to travel and how often it changes. Here's how I decide.

useState: The Default

const [count, setCount] = useState(0);

If the state lives and dies inside one component — a form input, a toggle, a modal's open/closed flag — this is all you need. Reaching for anything heavier here is premature.

useReducer: When Updates Get Complex

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

Once a component has several related pieces of state that update together, or update logic that's more than a one-liner, a reducer keeps the transitions predictable and testable in isolation.

Context: For Passing Down, Not for Frequent Updates

Context solves prop drilling — getting a value to a deeply nested component without threading it through every layer. It's a poor fit for state that changes often, because every consumer re-renders on every update, with no built-in way to subscribe to just part of the value.

Zustand: When You Need Shared State That Changes Often

import { create } from 'zustand';

const useCartStore = create((set) => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}));

function CartCount() {
  const items = useCartStore((state) => state.items);
  return <span>{items.length}</span>;
}

Components subscribe to only the slice of state they use, so unrelated updates don't cause unrelated re-renders — the problem Context has out of the box.

Quick Decision Guide

  • State used by one component → useState
  • Complex update logic within one component → useReducer
  • Rarely-changing values needed by many components (theme, auth user) → Context
  • Shared state that changes frequently across the app → Zustand (or similar)