codejump

AcademyReact

React Context and When to Use It

Context skips prop drilling for broadly needed values, but every consumer re-renders on any change to it — the cost most people miss until it shows up in a profiler.

3 min readUpdated

3 exercises

Waiting at the end of this page.

The mechanism

JSX
const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return <Button />; // never touches theme
}

function Button() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Click</button>;
}

Button reads theme directly from the nearest ThemeContext.Provider above it, however many layers away that is. Toolbar sits in between and never mentions theme at all — this is exactly the prop-drilling problem Context exists to remove for values a large, unpredictable part of the tree needs: the current theme, the signed-in user, the active locale.

What Context is not

Context is not a general-purpose state-management replacement for something like Redux or Zustand. It has no built-in way to update a value from deep in the tree without also passing a setter function down through it, no selectors, no way for a consumer to subscribe to only part of the value — a consumer either reads the whole context value or none of it. For state that changes often and is read by many components that each only care about a slice of it, a dedicated store, not Context, is the right tool.

The cost almost everyone misses

Every component that calls useContext on a given context re-renders whenever that context's value changes — even if the component only reads one field out of an object the provider is handing down:

JSX
function AppProvider({ children }) {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState('light');
  const [notifications, setNotifications] = useState([]);

  const value = { user, theme, notifications, setUser, setTheme, setNotifications };
  return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}

One context, three unrelated pieces of state. A component that only reads theme still re-renders every time notifications changes, because from React's point of view the context's value changed — it does not know, or care, that the component consuming it never looked at the field that actually moved. In an app where notifications arrive every few seconds, every themed component pays for a re-render it has no reason to need.

The fix: split by change frequency, not by topic

JSX
<UserContext.Provider value={user}>
  <ThemeContext.Provider value={theme}>
    <NotificationsContext.Provider value={notifications}>
      {children}
    </NotificationsContext.Provider>
  </ThemeContext.Provider>
</UserContext.Provider>

Three contexts instead of one. A component reading only theme now re-renders only when theme changes — notifications arriving no longer touches it. The grouping question is not "do these values belong to the same feature" but "do they change at the same rate": bundle values that change together, separate the ones that don't.

The second, smaller trap: a fresh value object every render

JSX
// New object every render, even when user and theme haven't changed
return <AppContext.Provider value={{ user, theme }}>{children}</AppContext.Provider>;

{ user, theme } is a new object literal on every call to this component, so every consumer sees "the value changed" — by reference — on every parent re-render, whatever user and theme actually did. Memoizing the value object so its identity only changes when its contents do closes this gap:

JSX
const value = useMemo(() => ({ user, theme }), [user, theme]);
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;

What to say in an interview

Context solves prop drilling for values a broad, unpredictable part of the tree needs — it does not solve frequent, fine-grained state updates, because every consumer re-renders on any change to the value, whole object at a time. The fix when that becomes a real cost is splitting one context into several by how often each piece changes, and memoizing the value object so an unrelated parent re-render doesn't hand consumers a "new" value for free.

Now practice it

2 challenges · 1 quiz

Reading this page is the cheap half. These are the exercises that make you use it.