State management is where React interviews separate people who can build components from people who can architect an app. The questions below test how you decide where state lives — local, lifted, Context, or a store — and how you keep it correct and cheap as an app grows. The strongest answers are decision frameworks with named trade-offs, not a preference for one library.
How to answer state-management questions
Answer with a cost ladder. Local state is free; lifting adds coupling; Context re-renders every consumer; a store adds boilerplate but adds selectors and tooling; a data layer replaces most "global state" for server data. When you get a scenario, place it on that ladder and justify the rung. Interviewers are listening for "as local as possible, as shared as necessary."
Q1. What are the categories of state in a React app?
Roughly four: local UI state (a toggle, an input), shared client state (data several components need), server/remote state (data fetched from an API), and URL state (route, query params). Each has a natural home — local state in the component, shared in a lifted parent or store, server state in a data cache, URL state in the router. Naming the categories up front frames every later answer.
Most "which library should I use" debates dissolve once you separate these. Server state does not belong in Redux; URL state does not belong in useState.
Interview note: Follow-up: "which category causes the most trouble?" Server state stored as client state — it forces you to hand-roll caching, staleness and refetching that a data layer already solves.
Q2. useState vs useReducer — how do you choose?
Use useState for simple, independent values. Reach for useReducer when the state has complex transitions, when multiple fields change together, when the next state depends on the previous in non-trivial ways, or when you want the transition logic testable in isolation.
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 });
The reducer centralises every way the state can change into one pure function, which is easier to reason about and unit test than several scattered setters.
Interview note: Trap: "reducers must be pure — why?" React may invoke them twice in development to catch side effects; a reducer that mutates or fetches breaks under Strict Mode and concurrent rendering.
Q3. What does "lifting state up" solve, and what is its cost?
When sibling components need the same state, you move it to their closest common parent and pass it down with update callbacks, giving a single source of truth. The cost is coupling and re-renders: the parent now owns the state and re-renders on every change, cascading to its subtree. Lifting is right until the state must reach many distant components — that is the signal to move to Context or a store.
Interview note: Follow-up: "how far do you lift?" To the closest common ancestor of the components that need it, never higher — lifting to the root by reflex is how everything becomes global.
Q4. How does Context fit into state management?
Context is a transport, not a store: it passes a value deep into the tree without prop drilling, and you pair it with useState or useReducer to hold that value. It suits low-frequency global data — theme, auth, locale. Its cost is that every consumer re-renders whenever the context value changes, so it is a poor fit for fast-changing state.
const AuthContext = createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const value = useMemo(() => ({ user, setUser }), [user]);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
Interview note: Trap: "why memoize the value?" A fresh object literal each render is a new reference, re-rendering every consumer even when the data did not change. See the Context API questions.
Q5. When do you add a state library like Redux or Zustand?
Add a store when you have large, frequently-updated shared state, complex update logic, or a need for selectors, devtools, middleware and time-travel debugging. Modern stores like Zustand and Redux Toolkit reduce the old boilerplate and let components subscribe to a slice, so a change to one field does not re-render components that read another. The Redux questions go deeper on the flow and toolkit.
The key advantage over Context is selective subscription: useContext re-renders on any value change, while a store lets a component read only the slice it needs.
Interview note: Follow-up: "Redux Toolkit vs plain Redux?" Redux Toolkit is the official, recommended way — it removes hand-written action types, immutable spreads (via Immer) and store setup, so most 'Redux is too much boilerplate' complaints are outdated.
Q6. What is server state, and how should it be managed?
Server state is data the backend owns — remote, shared, and able to become stale. It belongs in a caching data layer such as React Query (TanStack Query) or a framework's data loaders, which handle caching, deduplication, background refetching and invalidation. Storing it in client state means re-implementing all of that by hand.
const { data, isLoading, error } = useQuery({
queryKey: ["user", id],
queryFn: () => fetch(`/api/users/${id}`).then(r => r.json()),
});
Interview note: Trap: "why not just put fetched data in Redux?" You then own staleness, refetch, dedup and cache invalidation manually; a data layer is purpose-built for exactly this and shrinks your client store dramatically.
Q7. How do you prevent state updates from re-rendering too much?
Colocate state so updates stay local, split large Context into focused providers, memoize context values, and use a store with selectors so components subscribe to only the slice they read. Measure with the Profiler before optimising. The single highest-leverage move is colocation — pushing state down to the component that owns it so its updates never reach unrelated siblings.
Interview note: Follow-up: "one giant Context re-renders everything — fix?" Split it by update frequency, or switch to a store with selectors so consumers subscribe granularly.
Q8. How do you manage form state specifically?
For simple forms, controlled inputs with useState are fine; for larger forms, useReducer or a form library (React Hook Form, Formik) manages values, validation and submission with fewer re-renders. React Hook Form in particular keeps inputs uncontrolled under the hood to avoid re-rendering the whole form on every keystroke — a common performance answer. The forms handling questions cover this in depth.
Interview note: Trap: "controlled inputs feel slow on a big form — why?" Every keystroke updates state and re-renders the form; either isolate the field, debounce, or use an uncontrolled/form-library approach.
Q9. What is derived state, and why avoid storing it?
Derived state is a value you can compute from existing props or state — a filtered list, a total, a full name. Storing it in its own useState creates two sources of truth that drift apart and forces you to keep them in sync. Compute it during render instead, and memoize only if the computation is genuinely expensive.
// Don't store `total` in state — derive it:
const total = useMemo(() => items.reduce((s, i) => s + i.price, 0), [items]);
Interview note: Follow-up: "when is caching derived state with useMemo worth it?" Only when the computation is expensive or its stable reference is needed by a memoized child or an effect dependency — otherwise plain computation is cheaper.
How to prepare
Take one feature and place every piece of its state on the cost ladder: what is local, what is lifted, what is server data, what is URL state — then justify each home. That single exercise is the state-management interview in miniature. Practise the boundary calls until each has a one-line rationale: useState vs useReducer, Context vs store, client vs server state. When solid, drill into the Redux and Context API specifics for the tool-focused rounds, and use the React learning path to refresh data-layer patterns. A mock interview on architecture trade-offs is the fastest way to test whether your reasoning holds under follow-ups.
Frequently Asked Questions
When should I use useState vs useReducer?
Is Context a state management tool?
Context vs Redux — how do I choose?
What is server state and why keep it separate?
How do I stop state changes from re-rendering the whole app?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — View the Java Full Stack curriculum

