React State Management: useState vs useReducer vs Context vs Zustand

React State Management: useState vs useReducer vs Context vs Zustand
3 views

React state management is not a single tool — it’s a spectrum from local component state to global app state, and picking the wrong layer creates unnecessary complexity. useState for simple local state, useReducer for complex local state, Context for configuration, Zustand for app-wide state.

⚡ TL;DR: useState for simple primitive/object local state. useReducer when next state depends on previous in complex ways. Context for global config/theme/auth (not high-frequency updates). Zustand for global state with frequent updates — simpler than Redux, more capable than Context.

useState — simple local state — React state management

// Simple state: use useState
function Counter() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');
  // Independent pieces of state — separate useState calls
  return ;
}

// Object state: OK for related values
function Form() {
  const [form, setForm] = useState({name:'',email:'',age:''});
  const update = field => e =>
    setForm(prev=>({...prev,[field]:e.target.value}));
  return (
    <>
      
      
    
  );
}

useReducer — complex state transitions

// Use when: multiple sub-values, next state depends on previous
const initialState = { status:'idle', data:null, error:null };

function reducer(state, action) {
  switch(action.type) {
    case 'FETCH_START':   return {...state, status:'loading', error:null};
    case 'FETCH_SUCCESS': return {status:'success',data:action.payload,error:null};
    case 'FETCH_ERROR':   return {...state, status:'error', error:action.error};
    case 'RESET':         return initialState;
    default: throw new Error('Unknown action: '+action.type);
  }
}

function DataFetcher({url}) {
  const [state, dispatch] = useReducer(reducer, initialState);
  useEffect(() => {
    dispatch({type:'FETCH_START'});
    fetch(url).then(r=>r.json())
      .then(data=>dispatch({type:'FETCH_SUCCESS',payload:data}))
      .catch(err=>dispatch({type:'FETCH_ERROR',error:err.message}));
  }, [url]);
}

Context — global config, not frequent state

// GOOD: Context for slow-changing global values
const ThemeContext = createContext('light');
const AuthContext = createContext(null);

// BAD: Context for frequent updates (every update re-renders all consumers)
// Don't use Context for: shopping cart (updated on every add/remove)
// Do use Context for: theme, locale, auth user, feature flags

function App() {
  const [theme, setTheme] = useState('light');
  return (
    
      
    
  );
}

// Consume:
const {theme} = useContext(ThemeContext);

Zustand — simple global state

import {create} from 'zustand';

// Define store
const useCartStore = create((set,get) => ({
  items: [],
  total: 0,
  addItem: (item) => set(state => ({
    items: [...state.items, item],
    total: state.total + item.price
  })),
  removeItem: (id) => set(state => ({
    items: state.items.filter(i=>i.id!==id),
    total: state.total - (state.items.find(i=>i.id===id)?.price||0)
  })),
  clear: () => set({items:[],total:0})
}));

// Use in any component (no Provider needed!)
function CartIcon() {
  const {items} = useCartStore();
  return {items.length};
}
  • ✅ useState: primitive values, independent state pieces
  • ✅ useReducer: multiple related values, complex transitions
  • ✅ Context: slow-changing global (theme, auth, locale)
  • ✅ Zustand: global state with frequent updates — simpler than Redux
  • ❌ Don’t use Context for high-frequency updates — all consumers re-render
  • ❌ Don’t install Redux for small apps — overkill, Zustand is simpler

External reference: Zustand documentation.

Recommended Reading

→ Designing Data-Intensive Applications — The bible of distributed systems and production engineering at scale.

→ The Pragmatic Programmer — Timeless engineering wisdom every senior developer needs.

Affiliate links. We earn a small commission at no extra cost to you.

Free Weekly Newsletter

🚀 Join 2,000+ Senior Developers

Get expert-level JavaScript, Python, AWS, system design and AI secrets every week. Zero fluff, pure signal.

✓ No spam✓ Unsubscribe anytime✓ Expert-level only

Related: React Hooks Explained: useState, useEffect, useCallback, useMemo, useRef In Depth · Day 94: Change Management for Prompts · Day 64: Secret-Aware Context Filters

Related: React Performance Optimization: Every Technique From memo to Virtualization · Bedrock Prompt Management: Versioned System Prompts for Production · Amazon Q Developer: Custom Context Packs for Private Monorepos

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.