๐Ÿณ IntuitiveFE
Login
โ† All concepts

Context: Power & Pitfalls

โฑ๏ธ ~3-minute bite ยท solve the sandbox to master

0%lesson
๐Ÿง’

5-Year-Old Metaphor

โ€” The physical, real-world picture. No jargon.

๐Ÿ“ป Context = broadcast radio. Everyone in range receives it. Split contexts = separate stations โ€” subscribe only to the one you care about. External store = cable TV โ€” precise channel subscriptions.

One Big Context

AM radio

Everyone hears everything. Cart update โ†’ every consumer re-renders, even those showing only the theme.

Split Contexts

Multiple stations

ThemeContext, UserContext, CartContext. Each consumer subscribes only to the station it needs.

External Store

Cable with channels

Selector-based subscriptions. CartCount subscribes only to `s.items.length` โ€” nothing else triggers it.

Context is not a state management library

Context solves prop drilling โ€” passing values through many component layers. It does not solve re-render optimization. For fine-grained subscriptions to frequently-changing state, use Zustand, Jotai, or useSyncExternalStore directly.

๐ŸŽ›๏ธ

Interactive Sandbox

โ€” Move something, see it react instantly.

Pattern

Scenario

Sharing auth user, theme, and locale across the component tree without prop drilling

Re-renders

0
Context Basics
js
1// 1. Create context with a typed default
2const UserContext = createContext<User | null>(null);
3ย 
4// 2. Custom hook for safe consumption
5function useUser() {
6 const ctx = useContext(UserContext);
7 if (!ctx) throw new Error('useUser must be inside UserProvider');
8 return ctx;
9}
10ย 
11// 3. Provider wraps the subtree
12function UserProvider({ children }: { children: ReactNode }) {
13 const [user, setUser] = useState<User>(currentUser);
14 return (
15 <UserContext.Provider value={user}>
16 {children}
17 </UserContext.Provider>
18 );
19}
20ย 
21// 4. Any descendant accesses user โ€” no prop drilling
22function Avatar() {
23 const user = useUser();
24 return <img src={user.avatarUrl} alt={user.name} />;
25}
Gotcha: Throwing in the custom hook if context is null gives a clear error message. Without the throw, components silently receive null and crash with cryptic 'cannot read property of null' errors.
Insight: Always pair createContext with a custom hook. The hook provides the null guard, a good error message, and a stable import โ€” consumers import `useUser`, not `useContext(UserContext)`.
Explored:๐Ÿ“กโšกโœ‚๏ธ๐Ÿงฎ๐Ÿช
๐ŸŽฏ

Challenge

Trace the re-render cascade for each pattern. Can you explain why the count changes?

Try it
๐ŸŽฏ

Why Should I Care?

โ€” The exact interview question + the bug it kills.

Interview questions

Q: Why does context not prevent re-renders?

`useContext` subscribes to the context. When the Provider's value prop changes, every component that called `useContext(SomeCtx)` re-renders. There is no selector system โ€” you get the whole value or nothing. The only way to reduce re-renders is to split contexts, memoize the value, or use an external store with selectors.

Q: When should you NOT use context for state?

Don't use context for: frequently-updating state (more than a few times per second), state with many independent pieces that consumers partially use, server state (use TanStack Query), or client state with complex update logic. Use context for: theme, locale, auth, feature flags โ€” values that rarely change and all consumers care about equally.

Q: What is useSyncExternalStore and why was it added?

js
1// useSyncExternalStore โ€” React 18 hook for external stores
2// Used by Redux, Zustand, Jotai under the hood
3const value = useSyncExternalStore(
4 store.subscribe, // subscribe function
5 store.getSnapshot, // get current value (client)
6 store.getServerSnapshot // get value for SSR
7);
8ย 
9// Problem it solves: "tearing" in Concurrent Mode
10// Without it, different components in one render could
11// read different snapshots of an external store
12// โ†’ UI shows inconsistent state simultaneously
13// useSyncExternalStore gives React a consistent snapshot
๐Ÿ”ฌ

The Deep Dive

โ€” Spec refs, engine internals, the minutiae.

React context implementation

Context is implemented as a linked list of "consumers" on the fiber node. When a Provider renders with a new value, React traverses the fiber tree from the Provider downward, finding all fibers that have a `readContext(SomeCtx)` call. Those fibers are marked for re-render. This traversal is the source of the "all consumers re-render" behavior โ€” React has no way to know which part of the context value each consumer uses.

Context selector pattern (react-context-selector)

The library `use-context-selector` provides a `useContextSelector(ctx, selector)` hook that only triggers re-render when the selected value changes. It achieves this by storing all subscribers in a ref and calling each selector after context updates, comparing old/new โ€” only re-rendering if the selected value changed. React itself may adopt this pattern natively in a future release.

Zustand subscription model

Zustand uses `useSyncExternalStore` internally. The selector passed to `useStore(selector)` is called after every store update. If `Object.is(prevSelected, nextSelected)` returns true, the component doesn't re-render. This is why `useStore(s => s.items.length)` only re-renders when length changes โ€” even if individual items are added or modified without changing the count.

๐ŸŽค

Interview Questions

โ€” Real questions from real interviews โ€” with answers.

useContext subscribes to the entire context value โ€” any change to any field re-renders all consumers.

Every component that called useContext(Ctx) re-renders when the context value changes โ€” split contexts to limit who's subscribed.

Passing an object literal `value={{ user, logout }}` to the Provider creates a new object on every parent render.

Don't use Context for frequently-changing state, partially-subscribed state, server state, or complex update logic.

It gives React a consistent store snapshot to prevent 'tearing' โ€” UI inconsistency when Concurrent Mode pauses mid-render.

It stores all subscriber selectors in a ref, runs them after every context update, and only re-renders when the selected value changed.

๐ŸŽฎ

Memory Game

โ€” Quick quiz โ€” lock the concept in long-term memory.
1/4

What risk does a very deeply nested context Provider introduce?