Home/Learn/React/Choosing a State Management Approach

Choosing a State Management Approach

Advanced
Patterns

Most applications need far less global state than they install. Separate server data from client state first, and the remaining global state is usually small enough for a light store.

Overview

The question "which state manager should we use" is usually asked too early. Once server data moves to a query library and view state moves to the URL, what is left is genuinely global client state — a session, a theme, an open cart, a set of selected rows — and that is a small enough surface that a hundred-line store handles it. Redux still exists in large codebases and is worth being able to read, but a new project reaching for it before establishing that need is choosing ceremony over the problem.

Classify Before Choosing

Four kinds of state, three of which do not need a store at all.

Most "global state" is server state
// 1. SERVER state — fetched, cached, can change without you
//    -> TanStack Query. Not a store.
//    This is 70% of what people put in Redux.

// 2. URL state — filters, page, tab, search, selected id
//    -> useSearchParams. Shareable and refresh-proof for free.

// 3. LOCAL UI state — is this dropdown open, this input's draft
//    -> useState in the component that owns it

// 4. GLOBAL client state — session, theme, cart, sidebar collapsed
//    -> context for rarely-changing values
//    -> a store when it changes often or many components slice it

// Work through 1-3 honestly and 4 is often two or three values.

// The signal you actually need a store:
//   - many components read different SLICES of one changing object
//   - context re-renders are measurably hurting
//   - state must survive route changes and be updated from anywhere

A Light Store

Zustand is the current default: a hook, with selectors, and no provider.

Selectors are the difference from context
import { create } from 'zustand'

export const useCartStore = create((set, get) => ({
  items: [],
  add: (item) => set(s => ({ items: [...s.items, item] })),
  remove: (id) => set(s => ({ items: s.items.filter(i => i.id !== id) })),
  clear: () => set({ items: [] }),
}))

// Subscribe to a SLICE — this component re-renders only when the
// count changes, not when anything else in the store does
const count = useCartStore(s => s.items.length)
const add = useCartStore(s => s.add)

// That selector is the thing context cannot do, and the reason a
// store wins for frequently-changing shared state.

// Read outside React — in an interceptor, a socket handler, a test
useCartStore.getState().clear()

// Persistence, devtools and immer are middleware, not rewrites.

Redux, and When It Earns Its Weight

What to know for interviews, and what modern Redux actually looks like.

Redux Toolkit, and when it fits
// Redux Toolkit removed most of the old boilerplate
const cartSlice = createSlice({
  name: 'cart',
  initialState: { items: [] },
  reducers: {
    add: (state, action) => { state.items.push(action.payload) },  // immer
    remove: (state, action) => {
      state.items = state.items.filter(i => i.id !== action.payload)
    },
  },
})
export const { add, remove } = cartSlice.actions

const items = useSelector(s => s.cart.items)
const dispatch = useDispatch()

// The vocabulary interviewers use: store, action, reducer, dispatch,
// selector, middleware, and the single source of truth.

// Redux is genuinely worth it for: complex cross-cutting state,
// time-travel debugging, a large team needing enforced structure,
// or an existing codebase already using it.

// It is not worth it for: caching API responses, which is what most
// Redux code in the wild is doing by hand and badly.

Key Points to Remember

  • 1Classify state as server, URL, local or global before choosing a tool — most of it is not global
  • 2A query library handles server data, which is the majority of what ends up in stores
  • 3A store's selectors let components subscribe to slices, which is exactly what context cannot do
  • 4Zustand-style stores need no provider and can be read outside React, which suits interceptors and tests
  • 5Redux Toolkit is worth its weight for complex cross-cutting state or an existing codebase, not for caching responses

Interview Questions

Sign in to ask Aria
1

How do you decide whether a piece of state needs a global store?

Medium
2

Why is a query library not a state manager, and vice versa?

Hard
3

What can a store do that context cannot?

Hard

Ask Aria about Choosing a State Management Approach

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…