Case Study: Live Stock Dashboard (Part 2)
The case study on the Live Stock Dashboard illustrates a shift from a push-based data flow to a pull-based architecture to optimize rendering performance in React 19. By separating live stock updates from analytics data, components subscribe only to relevant slices of state, significantly reducing unnecessary re-renders. The use of selectors allows components to access only the data they need, while heavy computations are deferred using transitions, ensuring UI responsiveness. This architecture enhances concurrency, allowing urgent updates to be prioritized without blocking more intensive calculations, ultimately improving the overall performance of the application.
In the previous lesson, we reduced per-render cost using React.memo and useMemo. However, we did not reduce the frequency of invalidation. The DashboardProvider still emits a full broadcast every 200 ms. As long as the data flow remains push-based, React’s scheduler must continue re-rendering components that do not actually need an update.
Priority-based update architecture
To reach 60fps under sustained load, we invert the data flow and shift to what we call the fast lane architecture:
The data layer (pull, not push): Instead of storing live prices inside context, we place mutable price data in an external store outside the React tree. Components access this store using
useSyncExternalStorewith selectors. A selector defines exactly which slice of the store the component depends on.Result:
TickerPanelreceives only the live prices slice.AnalyticsPanelreceives only its derived slice.Shell components receive nothing and never re-render.
React compares the selector’s output to the previous one before rendering; if they match, the render is skipped entirely, even under concurrent replay.
The interaction layer (urgent lane vs. transition lane): Analytics computations remain heavy; O(n) on every change. We cannot speed up the math, but we can structure the pipeline so the UI remains responsive. Wrapping user-driven updates (filters, sorting) in
startTransitiontells React:Update the input immediately (urgent lane). Recompute analytics later (transition lane).
React 19 can now defer, interrupt, or replay the heavy computation without blocking keystrokes or scrolls.
Caption: High-frequency store updates feed atomic selectors, while heavy computations run in a transition lane without blocking urgent UI work.
Store updates no longer propagate through the entire tree. Each component subscribes only to the slice of state it depends on. When the store changes, React evaluates the selector. If the selected value is unchanged, React skips rendering the component entirely. This differs from context, where every consumer is notified of every update regardless of relevance.
Meanwhile, analytics recalculations run inside a transition. React commits urgent updates, such as input changes, immediately in the high-priority lane and defers heavy computations to a non-blocking, interruptible lane. This preserves smooth paint cadence and maintains consistent interaction responsiveness even under load.
With this architecture defined, let’s implement the External Store and replace our broadcast-driven Context with selector-based subscriptions that isolate updates and unlock true React 19 concurrency. In the previous version, AnalyticsPanel subscribed directly to state.stocks, and the tick loop replaces the entire stocks array every 200ms, so: