Choosing a State Management Approach
AdvancedMost 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.
// 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 anywhereA Light Store
Zustand is the current default: a hook, with selectors, and no provider.
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 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 AriaHow do you decide whether a piece of state needs a global store?
Why is a query library not a state manager, and vice versa?
What can a store do that context cannot?
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.