useContext and its performance: why one context can re-render your whole app
Context is for passing values deep without props, not for fast-changing state. Every consumer re-renders when the value changes, so split contexts, memoize the value, and know use(Context) in React 19.
Comes up in: React, Frontend system design
Learn#
Riya’s shop puts the user, the cart and the search text in one AppContext. Every keystroke in the search box re-renders the header, the avatar and 200 product cards, and typing feels slow on a mid-range phone.
Context passes a value deep into the tree without props. A provider sets the value; a consumer reads it with useContext. When the value changes, by Object.is, every consumer re-renders, even inside memo. A value written as a new object literal changes on every render:
import { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register(); // a DOM for Node; in a browser you already have one
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
const { createElement: h, createContext, useContext, useMemo, useState, memo, act } = await import("react");
const { createRoot } = await import("react-dom/client");
const UserContext = createContext(null);
let avatarRenders = 0;
const Avatar = memo(function Avatar() {
useContext(UserContext);
avatarRenders++;
return null;
});
let type;
function AppNewObject() {
const [user] = useState({ name: "Asha" });
const [search, setSearch] = useState("");
type = () => setSearch((s) => s + "a");
return h(UserContext.Provider, { value: { user } }, h(Avatar)); // a new object every render
}
function AppMemoized() {
const [user] = useState({ name: "Asha" });
const [search, setSearch] = useState("");
type = () => setSearch((s) => s + "a");
const value = useMemo(() => ({ user }), [user]); // the same object until user changes
return h(UserContext.Provider, { value }, h(Avatar));
}
for (const App of [AppNewObject, AppMemoized]) {
avatarRenders = 0;
const root = createRoot(document.createElement("div"));
await act(() => root.render(h(App)));
for (let i = 0; i < 5; i++) await act(() => type()); // typing in a search box
console.log(App.name, "→ Avatar rendered", avatarRenders, "times");
}Output (verified)
AppNewObject → Avatar rendered 6 times AppMemoized → Avatar rendered 1 times
State
- value (render 1) = object #1
App holds the search text and the user. It passes value={{ user }} to UserContext.Provider.
Read the steps as text
- App holds the search text and the user. It passes value={{ user }} to UserContext.Provider.
- The user types a letter. App re-renders and the literal { user } creates object #2.
- React compares the old and new value with Object.is. They differ, so every component that reads UserContext re-renders, even through memo.
- With useMemo(() => ({ user }), [user]) the value stays object #1, so Avatar renders once in total.
Predict#
Predict first
A context holds { user, cart }. The cart changes. A component reads only user from the context. Does it re-render?
Context has no selectors. Any change to the value re-renders every consumer, whatever part it reads, and memo cannot stop it. Split user and cart into two contexts, or use a store with selectors.
Interview angle#
Say it like this — about 30 seconds
“Context is for dependency injection: values many components need that change rarely, such as the theme, the current user or a service. When the provider’s value changes by Object.is, every consumer re-renders, even through memo, and context has no selectors. So I memoize the value object, split contexts by how often they change, and keep fast-changing state local or in a store with selectors, such as Zustand. In React 19, use(Context) reads context and can be called conditionally.”
Follow-up Why can context cause performance problems? How do you fix them?
Follow-up Why put dispatch in its own context?
Follow-up What is different about use(Context)?
Practice#
Guided
Build a small store with selectors: the way libraries let a component listen to only the part it needs.
Done when
setStatecreates a new state object; it never mutates the old one.- A subscriber runs only when its selected value changes.
- Unsubscribing stops notifications.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
When do context consumers re-render?
Whenever the provider's value changes by Object.is.
Can memo stop a context consumer from re-rendering?
No. Context changes go through memo.
Name two fixes for slow context.
Memoize the value; split contexts by how often they change.
What can use(Context) do that useContext cannot?
Be called inside conditions and loops.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Check yourself: What is context best used for?
Values many components need that change rarely, such as theme, user or services.
How it works underneathSenior rounds
Object.is. Wrap object values in useMemo and functions in useCallback, or the React Compiler does it for you Source: React: Optimizing re-renders when passing objects and functions.Check yourself: Why does a new object literal as value re-render consumers every time?
It is a new reference each render, so Object.is says it changed.
Edge cases and trapsWhere people slip
ThemeContext that rarely changes, a CartContext that changes often, and a separate context for dispatch, which is stable.Check yourself: Why split state and dispatch into two contexts?
Components that only dispatch then never re-render on state changes.
In productionStaff-level answers
use(Context) reads context and may be called conditionally Source: React: use.useSyncExternalStore, RCT.02.03) re-renders only the components whose slice changed.Check yourself: How do stores like Zustand avoid context re-renders?
Components subscribe with selectors and re-render only when their slice changes.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- Why can context cause performance problems? How do you fix them?