Splitting State and Dispatch
Diagnose the problem: Context Thrashing
Welcome to the performance module. In this unit, you will identify and fix a common bottleneck in React apps that use Context with a high-frequency loop. Our game dispatches a TICK every second, which can force large parts of the tree to re-render. The root cause is context thrashing: when a context value changes often, every consumer re-renders — even if it only triggers actions and never reads state.
Your goal is to refactor the architecture so that components that only trigger actions no longer re-render on every state update.
Split State and Dispatch into separate contexts
We separate read-heavy state from write-only dispatch. The dispatch function from useReducer is a stable reference, so putting it in its own context prevents “write-only” components from re-rendering every second.
Explanation:
- Two contexts: one for state, one for dispatch.
GameDispatchContext.ProviderwrapsGameStateContext.Providerso both are available.- Because
dispatchis stable, components that consume only dispatch won’t re-render onTICK. - Components that read state still re-render as needed when state changes.
Create focused hooks for safe, clear usage
These hooks ensure components consume only what they need and fail fast if used outside the provider.
Explanation:
useGameStatereturns the current game state;useGameDispatchreturns dispatch.- Each throws a clear error if called outside
GameProvider. - Components that only need to fire actions should use
useGameDispatchexclusively, avoiding state-driven re-renders.
Summary and next steps
You profiled the app to spot context thrashing and refactored the architecture by splitting state and dispatch. Now, components that only dispatch actions can remain calm during the 1-second tick storm. Next, you will get hands-on experience and see the re-render count drop as you apply this pattern in practice.
