Search⌘ K
AI Features

Selectors and Fine-Grained Reactivity

Context segmentation in React isolates updates by domain but lacks a selection mechanism, leading to unnecessary re-renders when unrelated state changes. React 19 introduces fine-grained reactivity through useSyncExternalStore, allowing components to subscribe to specific slices of state via selectors. This method minimizes re-renders by only updating components whose selected state has changed, enhancing performance during concurrent rendering. Best practices emphasize using context for static data, keeping selectors narrow and stable, and avoiding heavy computations within selectors to maintain efficient and predictable React applications.

Context segmentation isolates updates by domain, but it does not isolate updates within that domain. This limitation creates the selectivity gap. When a context provides { A, B }, a component that only needs A must still re-render when B changes, because context has no native selection mechanism. Under React 19 concurrency, where renders may be replayed, interrupted, or aborted, this gap becomes significantly more expensive. Each context update forces broad invalidation, and React can end up re-running that invalidated work multiple times before a commit is made.

When segmentation is pushed too far, the result is provider sprawl, where each small concern ends up with its own provider. This approach preserves correctness, but it does not eliminate unnecessary re-renders and adds structural complexity.

The fine-grained reactivity

The advanced solution is to step outside the React tree. useSyncExternalStore (uSES) enables components to subscribe to exactly the slice of state they depend on. Instead of React pushing updates to every consumer, components “pull” the data they truly need via concise selector functions. React re-runs the selector when the store notifies of a change; if the selector’s output is referentially stable, React skips the component before the render phase even begins. This achieves fine-grained reactivity: updating field X only re-renders components whose selector depends on X, delivering O(1) invalidation even under React 19’s speculative rendering.

Context push vs. selector pull
Context push vs. selector pull

In the diagram, context broadcasts updates to all consumers, while selector-based stores re-render only components whose selected slice has changed. In Scenario A, context propagates each update to all consumers, including those that do not read the updated field. Under React 19 concurrency, this broadcast can trigger unnecessary speculative renders that replay multiple times before a commit.

In Scenario B, each component pulls a minimal slice via a selector. When the store updates, React evaluates the selector and compares the new output with the previous one. If the result is ...