facebook/reactMITd083ec1Report / request removal

Context API

Context lets a component publish a value once and lets descendants read it without receiving that value through every intermediate component’s props. The public entry point is createContext, while the reconciler maintains the current value as it renders providers and consumers.

This exists because the value must follow the rendered fiber tree, including deeply nested descendants and subtrees that do not directly participate in passing props. The reconciler therefore combines a render-time value stack with per-fiber dependency records and explicit propagation when a provider changes.

Sources: packages/react/src/ReactContext.js:14-46, packages/react-reconciler/src/ReactFiberNewContext.js:82-127, packages/react-reconciler/src/ReactFiberNewContext.js:580-627, packages/react-reconciler/src/ReactFiberNewContext.js:129-153, packages/react-reconciler/src/ReactFiberNewContext.js:214-377

Core concepts

Context object

A context object is the shared record created by createContext, containing default values, renderer-specific current values, and linked Provider and Consumer objects.

createContext initializes _currentValue and _currentValue2 to the default, sets _threadCount to 0, points Provider back to the context, and creates a Consumer containing _context.

FieldMeaning
_currentValueCurrent value for the primary renderer.
_currentValue2Current value for the secondary renderer.
_threadCountCount of concurrent renderer support within a renderer.
ProviderThe provider object associated with the context.
ConsumerA consumer object that refers back to the context.

These fields are initialized together so the public object can support renderer-specific storage while exposing one context abstraction.

Sources: packages/react/src/ReactContext.js:14-46

Provider scope

A provider is the fiber boundary that temporarily makes its nextValue current while React renders its descendants. pushProvider saves the previous value in valueCursor, writes the new value into the renderer-specific field, and records development renderer state when enabled.

When React leaves that provider, popProvider restores the saved value from valueCursor and pops the renderer bookkeeping. This stack discipline is why a nested provider can override a parent value only for its own subtree.

Sources: packages/react-reconciler/src/ReactFiberNewContext.js:82-127, packages/react-reconciler/src/ReactFiberNewContext.js:129-153

Context dependency

A context dependency is a record attached to a consumer fiber when that fiber reads a context. It stores the context, the value observed during render, and a link to the next dependency.

prepareToReadContext sets currentlyRenderingFiber, clears lastContextDependency, and resets the work-in-progress fiber’s firstContext list before a new read sequence. readContextForConsumer then creates the dependency record, attaches the first record to consumer.dependencies, and marks the consumer with NeedsPropagation.

Sources: packages/react-reconciler/src/ReactFiberNewContext.js:580-627, packages/react-reconciler/src/ReactFiberNewContext.js:539-551, packages/react-reconciler/src/ReactFiberNewContext.js:580-625

Context propagation

Propagation is the update path that finds fibers depending on changed contexts and marks them for work. A matching dependency merges renderLanes into the consumer and its alternate, then schedules the ancestor path through scheduleContextWorkOnParentPath.

The reconciler does not need to pass the value through intermediate component props: each consumer reads the current value during render and leaves behind a dependency record that later propagation can match.

Sources: packages/react-reconciler/src/ReactFiberNewContext.js:214-377, packages/react-reconciler/src/ReactFiberNewContext.js:580-627

How a value reaches a deeply nested consumer

The following data flow shows how provider scope becomes a dependency on a distant fiber.

Context value flow — How does a provider value reach a deeply nested consumer?

Evidence

The important boundary is render traversal: pushProvider changes the current renderer value before descendants read it, and popProvider restores the parent value afterward. A deeply nested useContext call ultimately reaches dispatcher.useContext, which is the public hook hand-off into the active render implementation.

During reconciliation, readContextDuringReconciliation prepares the consumer when no fiber is currently being rendered, then calls readContextForConsumer. readContextForConsumer selects _currentValue or _currentValue2, creates a context item with that value, and stores it on the consumer fiber’s dependency list.

const contextItem = {
  context: context as any as ReactContext<mixed>,
  memoizedValue: value,
  next: null,
};
...
consumer.dependencies = {
  lanes: NoLanes,
  firstContext: contextItem,
};
consumer.flags |= NeedsPropagation;

This records what the fiber observed rather than copying the value through each component between the provider and consumer.

Sources: packages/react-reconciler/src/ReactFiberNewContext.js:82-127, packages/react-reconciler/src/ReactFiberNewContext.js:129-153, packages/react/src/ReactHooks.js:53-63, packages/react-reconciler/src/ReactFiberNewContext.js:569-578, packages/react-reconciler/src/ReactFiberNewContext.js:580-625

How provider changes bypass a consumer bailout

When reconciliation reaches a subtree, propagateParentContextChanges walks upward through parent fibers and collects providers whose new value is not equal to their old value. It then calls propagateContextChanges over the children with the collected contexts.

The propagation walk checks each fiber’s dependencies.firstContext list. When a dependency refers to one of the changed contexts, it merges the render lanes into the consumer and alternate, then schedules the path from the consumer toward the propagation root.

That scheduling updates childLanes on each ancestor and its alternate until it reaches propagationRoot. This keeps the changed subtree visible to the work scheduler even when intermediate ancestors would otherwise appear idle.

The lazy path deliberately stops descending below a matched consumer because the consumer’s own render will visit its children. Full propagation instead continues through the tree when forcePropagateEntireTree is true.

A bailout therefore does not permanently hide a context update. If props and state have already bailed out, checkIfContextChanged compares each recorded memoizedValue with the current renderer value and returns true when a context differs. The separate DidPropagateContext flag prevents repeated propagation through the same subtree, while NeedsPropagation allows deferred propagation to be revisited.

The following sequence isolates the read-side call order used when reconciliation needs to read context for a fiber.

Context read sequence — How is a context dependency attached during reconciliation?

Evidence

Sources: packages/react-reconciler/src/ReactFiberNewContext.js:411-478, packages/react-reconciler/src/ReactFiberNewContext.js:481-490, packages/react-reconciler/src/ReactFiberNewContext.js:228-261, packages/react-reconciler/src/ReactFiberNewContext.js:155-195, packages/react-reconciler/src/ReactFiberNewContext.js:263-273, packages/react-reconciler/src/ReactFiberNewContext.js:515-537, packages/react-reconciler/src/ReactFiberNewContext.js:492-511

How context storage is attached to a fiber

The active render fiber is held in currentlyRenderingFiber, while lastContextDependency tracks the tail of the dependency list being built. prepareToReadContext clears both the active list position and the work-in-progress fiber’s previous firstContext pointer before reading.

The first context read creates consumer.dependencies with lanes and firstContext; later reads append another context item through lastContextDependency.next. Thus one fiber can retain multiple context dependencies, each with its own observed value for later comparison.

React also resets these render globals before yielding so readContext cannot be used outside the render phase. In development, disallowed reads are tracked separately and readContext reports an error before delegating to readContextForConsumer.

Sources: packages/react-reconciler/src/ReactFiberNewContext.js:539-551, packages/react-reconciler/src/ReactFiberNewContext.js:595-625, packages/react-reconciler/src/ReactFiberNewContext.js:580-627, packages/react-reconciler/src/ReactFiberNewContext.js:60-68, packages/react-reconciler/src/ReactFiberNewContext.js:70-74, packages/react-reconciler/src/ReactFiberNewContext.js:553-567

How it connects

  • The public useContext hook belongs to the React package and hands control to the current dispatcher; the reconciler implements the fiber-side read and dependency tracking.
  • createContext is part of the exported React surface, while provider scoping and propagation live in react-reconciler. See Public API Reference and Fiber Architecture.
  • Context reads occur during the interruptible render work, and changed consumers are marked with lanes and ancestor childLanes; scheduling and lane semantics are covered in Render Phase and Lanes & Priority.
  • The provider’s push/pop behavior is reconciler state management rather than host mutation, so its relationship to DOM updates is indirect through Commit Phase.

Sources: packages/react/src/ReactHooks.js:53-63, packages/react-reconciler/src/ReactFiberNewContext.js:580-627, packages/react/src/ReactContext.js:14-46, packages/react-reconciler/src/ReactFiberNewContext.js:82-127, packages/react-reconciler/src/ReactFiberNewContext.js:214-377, packages/react-reconciler/src/ReactFiberNewContext.js:155-195, packages/react-reconciler/src/ReactFiberNewContext.js:129-153

Key takeaways

  • A provider places its value in renderer-specific context storage while its subtree renders, then restores the previous value.
  • A consumer fiber stores each context read in dependencies.firstContext with the value observed during render.
  • Provider changes are propagated by matching dependency records, marking consumers, and scheduling ancestor paths.
  • A props/state bailout still checks recorded context values, so a changed context can invalidate the consumer.

Sources: packages/react-reconciler/src/ReactFiberNewContext.js:82-127, packages/react-reconciler/src/ReactFiberNewContext.js:129-153, packages/react-reconciler/src/ReactFiberNewContext.js:580-627, packages/react-reconciler/src/ReactFiberNewContext.js:214-377, packages/react-reconciler/src/ReactFiberNewContext.js:155-195, packages/react-reconciler/src/ReactFiberNewContext.js:515-537

Want this for your repos?

Try Angada AI Wiki