Skip to main content
Practice Problems

State management approaches in React

State Management in React

Choosing the right state management approach depends on what kind of state you're managing and how widely it's shared. React offers built-in options, and the ecosystem provides powerful external libraries.


Types of State

TypeExamplesBest approach
Local/UI stateForm inputs, toggles, modalsuseState, useReducer
Shared stateTheme, auth, cartContext, Zustand, Redux
Server stateAPI data, cacheTanStack Query, SWR
URL stateFilters, paginationURL params, search params
Form stateComplex form validationReact Hook Form, Formik

Built-in: useState

tsx
function Counter() { const [count, setCount] = useState(0); return <button onClick={() => setCount(c => c + 1)}>{count}</button>; }

Best for: Simple, local component state.

Built-in: useReducer

tsx
type Action = { type: "increment" } | { type: "decrement" } | { type: "reset" }; function reducer(state: number, action: Action): number { switch (action.type) { case "increment": return state + 1; case "decrement": return state - 1; case "reset": return 0; } } function Counter() { const [count, dispatch] = useReducer(reducer, 0); return ( <div> <span>{count}</span> <button onClick={() => dispatch({ type: "increment" })}>+</button> <button onClick={() => dispatch({ type: "decrement" })}>-</button> </div> ); }

Best for: Complex local state with multiple actions.

Built-in: Context API

tsx
const ThemeContext = createContext<"light" | "dark">("light"); function ThemeProvider({ children }: { children: React.ReactNode }) { const [theme, setTheme] = useState<"light" | "dark">("light"); return ( <ThemeContext.Provider value={theme}> {children} </ThemeContext.Provider> ); } function ThemedButton() { const theme = useContext(ThemeContext); return <button className={theme}>Click me</button>; }

Best for: Infrequently changing global values (theme, locale, auth).

⚠️ Limitation: all consumers re-render when the context value changes.

Zustand (Lightweight Store)

tsx
import { create } from "zustand"; interface CartStore { items: Product[]; addItem: (item: Product) => void; removeItem: (id: string) => void; total: () => number; } const useCartStore = create<CartStore>((set, get) => ({ items: [], addItem: (item) => set(state => ({ items: [...state.items, item] })), removeItem: (id) => set(state => ({ items: state.items.filter(i => i.id !== id) })), total: () => get().items.reduce((sum, i) => sum + i.price, 0), })); // Usage — only subscribes to what it reads function CartCount() { const count = useCartStore(state => state.items.length); return <span>{count}</span>; }

Best for: Simple to medium shared state. Minimal boilerplate, great performance.

Redux Toolkit

tsx
import { createSlice, configureStore } from "@reduxjs/toolkit"; const counterSlice = createSlice({ name: "counter", initialState: { value: 0 }, reducers: { increment: (state) => { state.value += 1 }, decrement: (state) => { state.value -= 1 }, }, }); const store = configureStore({ reducer: { counter: counterSlice.reducer } });

Best for: Large apps with complex state, middleware, devtools.

TanStack Query (Server State)

tsx
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; function Users() { const { data, isLoading, error } = useQuery({ queryKey: ["users"], queryFn: () => fetch("/api/users").then(r => r.json()), }); if (isLoading) return <Spinner />; return <UserList users={data} />; }

Best for: Server state — caching, background refetching, optimistic updates, pagination.

Decision Guide

Is it local to ONE component? → useState / useReducer Is it shared by a FEW nearby components? → Lifting state up Is it global but changes RARELY (theme, auth)? → Context API Is it global and changes OFTEN? → Zustand (simple) or Redux Toolkit (complex) Is it data from an API? → TanStack Query / SWR

Important:

There's no single best solution — use the right tool for each type of state. Start with useState, lift state up when needed, add Context for globals, use TanStack Query for server data, and reach for Zustand or Redux only when truly needed. Avoid putting everything in global state.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems