Skip to solution
mediumFrontend

What is the Context API in React and when would you use it?

913 views
01

Understand the problem

Question presented to candidate: "What is the Context API, and how do you decide whether a value belongs in it?"

What a strong answer should cover:

  • createContext makes a context; a provider supplies a value; any descendant reads it with useContext.
  • It solves prop drilling — delivering a value without threading it through intermediate components.
  • It is a transport mechanism, not a state manager. It does not store or update anything; the state still lives in a component or a store, and Context merely carries it.
  • The cost: every consumer re-renders when the provider value changes, and memo does not stop that.
  • So it suits values that are read widely and change rarely: theme, locale, current user, feature flags, a stable dispatch.
  • It suits badly: anything changing frequently, like form input or cursor position.
  • The default value is only used when a consumer has no provider above it — useful for tests and for catching missing providers.
  • Convention: wrap the useContext call in a custom hook that throws when the provider is missing.
  • Multiple small contexts beat one large object, because the granularity of re-rendering follows the granularity of the contexts.

Clarifying questions expected:

  • "How often does this value change, and how many components read it?"
  • "Have we tried composition first?" — often it removes the need entirely.

Code / implementation expected: Yes — a provider, a custom consumer hook with a missing-provider guard, and a memoised value.

state managementcontext apipropsglobal state
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for React interviews — assumes props and state. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The default-value and re-render behaviour below were measured on React 19.2.8. Th

Solution ready — 2 min read

Classified // press E to declassify

04

Explore the playground snippets

A provider with a memoised value, and a guarded consumer hook
Run Playground
import { createContext, useContext, useState, useMemo, useCallback, memo } from "react";

// null as the default so a missing provider is detectable rather than silently
// giving every consumer a plausible-looking fallback.
const ThemeContext = createContext(null);

// The custom hook keeps the context object private and turns "rendered outside
// the provider" into a clear error instead of a confusing undefined.
function useTheme() {
  const ctx = useContext(ThemeContext);
  if (ctx === null) throw new Error("useTheme must be used inside a ThemeProvider");
  return ctx;
}

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");
  const toggle = useCallback(() => setTheme((t) => (t === "light" ? "dark" : "light")), []);

  // Without useMemo this object is new on every render, so every consumer
  // re-renders even when the theme has not changed.
  const value = useMemo(() => ({ theme, toggle }), [theme, toggle]);

  return <ThemeContext value={value}>{children}</ThemeContext>;
}

let panelRenders = 0;
const ThemedPanel = memo(function ThemedPanel() {
  panelRenders++;
  const { theme, toggle } = useTheme();
  return (
    <div style={{
      padding: 12, borderRadius: 8,
      background: theme === "dark" ? "#222" : "#f4f4f4",
      color: theme === "dark" ? "#eee" : "#222",
    }}>
      <p style={{ margin: "0 0 8px" }}>
        theme: <strong>{theme}</strong> · panel renders: <strong>{panelRenders}</strong>
      </p>
      <button onClick={toggle}>toggle theme</button>
    </div>
  );
});

// Deliberately rendered OUTSIDE the provider to show the guard firing.
function Unguarded() {
  try {
    useTheme();
    return <p>read the theme</p>;
  } catch (e) {
    return <p style={{ color: "crimson", fontSize: 13 }}>caught: {e.message}</p>;
  }
}

export default function App() {
  const [tick, setTick] = useState(0);

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6, maxWidth: 420 }}>
      <ThemeProvider>
        <ThemedPanel />
      </ThemeProvider>

      <p style={{ marginTop: 12 }}>
        <button onClick={() => setTick((t) => t + 1)}>
          re-render the app ({tick})
        </button>
      </p>

      <Unguarded />

      <p style={{ color: "#666", fontSize: 13 }}>
        Press the app re-render button: the panel counter stays put, because the
        memoised value keeps the same identity. Toggle the theme and it moves.
        The last line shows the custom hook catching a missing provider.
      </p>
    </div>
  );
}
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 42 of 119 decoded in the React.js track. One more won't hurt.

Back to track