The Context API is a small feature with a large interview footprint, because it is easy to use and easy to misuse. Interviewers want to know that you can wire it up, but mostly they want to hear the performance story — why every consumer re-renders when the value changes, and how you keep that from turning Context into a bottleneck. The questions below cover the mechanics and, more importantly, the judgement.
How to answer Context questions
Say what Context is and what it is not. It is a transport that passes a value deep into the tree without prop drilling; it is not a state manager, and it has no way to subscribe to part of a value. Every strong answer here comes back to one fact: consumers re-render when the Provider value changes by reference. Lead with that and the follow-ups about memoization and splitting contexts answer themselves.
Q1. What problem does Context solve, and when do you reach for it?
Context passes data to deeply nested components without prop drilling — threading a prop through intermediate components that do not use it. Reach for it with genuinely global, low-frequency data: theme, authenticated user, locale, feature flags. For data only a few nearby components need, lifting state or composition is simpler.
The trap is treating Context as the first answer to any shared state. It shines for data that is truly cross-cutting and changes rarely; it hurts for data that changes often, because of the re-render behaviour in Q3.
Interview note: Follow-up: "Context vs prop drilling for two levels?" Two levels of drilling is usually clearer than a context; add Context when the depth or number of consumers makes drilling genuinely painful.
Q2. How do you create and consume a Context?
Create it with createContext(defaultValue), provide a value with <MyContext.Provider value={...}> wrapping the subtree, and read it in any descendant with the useContext(MyContext) hook. The default value is only used when a consumer has no Provider above it.
const ThemeContext = createContext("light");
function App() {
const [theme, setTheme] = useState("dark");
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext); // no prop drilling
return <div className={theme}>...</div>;
}
Interview note: Follow-up: "when is the default value used?" Only when a component calls
useContextwith no matching Provider above it — useful for standalone tests, and a signal you forgot a Provider if you see it in the app.
Q3. Why do all consumers re-render when the context value changes?
A component calling useContext subscribes to the entire context value. When the Provider's value changes by reference, React re-renders every consumer, regardless of whether they use the part that changed. There is no built-in partial subscription — this is Context's defining performance characteristic.
The most common accidental version: passing an inline object as the value. value={{ user, setUser }} creates a new object every render, so every consumer re-renders on every Provider render, even when user never changed.
Interview note: Trap: "how do you subscribe to just one field of a context?" You cannot with plain Context — you split contexts or use a store with selectors. Recognising this limit is the point of the question.
Q4. How do you fix unnecessary Context re-renders?
Three tactics: memoize the Provider value with useMemo so its reference is stable, split one large context into several focused ones, and separate rarely-changing data from frequently-changing data so consumers subscribe only to what they need.
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const value = useMemo(() => ({ user, setUser }), [user]); // stable reference
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
Interview note: Follow-up: "you memoized the value but consumers still re-render on unrelated changes — why?" The context still holds multiple fields; a change to any of them changes the value. Split the context so each concern updates independently.
Q5. Why split one context into several?
Because a single context holding user, theme and a live-updating value re-renders every consumer whenever any part changes. Splitting into AuthContext, ThemeContext and so on means a theme change no longer re-renders auth consumers. Group by update frequency and by which components actually need each slice.
This is the standard senior answer to "our app re-renders too much and it uses Context." The fix is architectural — smaller, purpose-built contexts — not more memoization.
Interview note: Trap: "isn't one context simpler?" Simpler to write, worse to run at scale; the re-render cost grows with consumers. Split before it becomes a performance problem.
Q6. Context vs Redux (or another store) — how do you choose?
Context is a transport for a value with no selectors, middleware or devtools, re-rendering all consumers on change — ideal for small, rarely-changing global data. A store like Redux or Zustand offers selective subscription (components read only their slice), middleware and tooling — ideal for large, frequently-updated shared state. They are different tools; a common architecture uses Context for theme/auth and a store or data layer for everything busy.
The Redux questions and state management questions expand this decision.
Interview note: Follow-up: "can you build selectors on top of Context?" Not natively — libraries exist that add selector-style subscription to Context, but at that point a purpose-built store is usually the cleaner choice.
Q7. How do you combine Context with useReducer for app-wide state?
Hold the state with useReducer inside a Provider, then expose the state and dispatch through Context. Components read state from one context and dispatch actions from another (or the same), giving a lightweight Redux-like pattern without the library — suitable for medium apps with moderate update frequency.
const StateContext = createContext(null);
const DispatchContext = createContext(null);
function Store({ children }) {
const [state, dispatch] = useReducer(reducer, initial);
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>{children}</DispatchContext.Provider>
</StateContext.Provider>
);
}
Splitting state and dispatch into two contexts is deliberate: dispatch is stable, so components that only dispatch never re-render on state changes.
Interview note: Follow-up: "why two contexts here?" So dispatch-only components (buttons) don't re-render when state changes — dispatch's reference is stable, state's is not.
Q8. What are common Context mistakes interviewers watch for?
Passing an inline object/array as the value (new reference every render), putting fast-changing state in a wide context, using Context where local state or composition would do, and forgetting the Provider so consumers silently get the default value. Each maps directly to a re-render or correctness bug, which is why they make good interview probes.
Interview note: Trap: "your
useContextreturns the default value in production — what happened?" A consumer rendered outside its Provider; wrap the subtree correctly, or throw from a custom hook when the context is null to fail loudly.
Q9. How do you make a safe custom hook around a Context?
Wrap useContext in a custom hook that throws a clear error when the context is missing, so misuse fails loudly instead of silently returning a default. This is a small idiom that reads as production experience.
function useAuth() {
const ctx = useContext(AuthContext);
if (ctx === null) throw new Error("useAuth must be used within <AuthProvider>");
return ctx;
}
Interview note: Follow-up: "why throw instead of returning the default?" A silent default hides a wiring bug that surfaces as confusing behaviour later; throwing turns it into an immediate, obvious error.
How to prepare
Build an auth or theme context end to end: createContext, a Provider with a memoized value, a useContext consumer, and a safe custom hook — then deliberately introduce the inline-value bug and watch everything re-render in the Profiler. Seeing the re-render cascade once fixes the concept permanently. Rehearse the "Context is a transport, not a store" framing and the split-context fix, since those are the highest-value answers. Pair this with the state management and Redux sets for the tool-choice rounds, refresh patterns via the React learning path, and use a mock interview to practise the performance follow-ups out loud.
Frequently Asked Questions
What problem does the Context API solve?
Why do all consumers re-render when context changes?
How do you stop unnecessary Context re-renders?
Is Context a replacement for Redux?
Can you have multiple contexts in one app?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Check the Java Full Stack training details

