React Native State Management: Context vs External Stores at Scale
Quick answer
Quick answer: React Context is fine for low-frequency, app-wide values like theme, locale, and the signed-in user. It re-renders every consumer whenever its...
Quick answer: React Context is fine for low-frequency, app-wide values like theme, locale, and the signed-in user. It re-renders every consumer whenever its value changes, so it scales poorly for frequently updated state. For fast-changing or widely shared state, use an external store with selector-based subscriptions so components re-render only for the data they read.
Why Does Context Cause Re-Renders?
When a Context provider receives a new value, React re-renders every component that consumes that context, regardless of which part of the value it actually uses. If you store a large object with many fields in one context, a change to any field re-renders every consumer. The comparison is by reference, so creating a new object on each render triggers updates even when the content is identical.
On a fast development machine you may never notice. On a mid-range Android phone, the JavaScript thread has far less headroom, and cascading re-renders show up as dropped frames, laggy typing, and slow transitions. Mobile apps also have more screens mounted at once, since navigation stacks keep previous screens alive, so one update can reach many components you cannot see.
Context is not broken. It is a dependency injection mechanism, designed to pass values down the tree without prop drilling. Problems begin when it is used as a general-purpose state container for data that changes often.
How Do You Make Context Work Well?
For values that change rarely, Context is simple and requires no extra dependency. A few habits keep it efficient:
- Split contexts by concern, so a theme change does not re-render components that only need the user.
- Separate state from actions, so components that only dispatch updates do not re-render when state changes.
- Memoize the provider value so it changes only when its content changes.
- Keep high-frequency values, such as text input, scroll position, and timers, out of Context.
Memoization is straightforward, and it prevents needless updates when a parent re-renders for unrelated reasons.
const value = useMemo(() => ({ user, signOut }), [user, signOut]);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
When Do External Stores Make More Sense?
External stores keep state outside the React tree and let components subscribe to slices of it. With a selector, a component re-renders only when the selected value changes, so updating one item in a large list does not wake unrelated screens. This model fits shopping carts, form drafts, playback state, chat messages, and anything updated many times per second.
They also help with logic that lives outside components, such as background tasks, notification handlers, or network layers that need to read and update state without being inside a React render. And they usually make persistence and debugging easier, since state changes pass through a single, inspectable place.
The costs are real. You add a dependency and a pattern the team must learn, and you must decide on conventions for structuring state and side effects. Choose a library by reading its documentation on selectors, persistence, and how it interacts with your React version, rather than by popularity alone.
What About Server State?
A large share of what apps call state is actually data fetched from a server. Caching, refetching, invalidation, and loading states are a different problem from client state like a selected tab or a draft message. Mixing them in one global store leads to hand-written caching code and stale data bugs.
A clean split works well: use a data-fetching layer for server state, a small store or local component state for client state, and Context only for stable, app-wide configuration. Keep state as local as possible, because state used by one screen should live in that screen, not in a global store.
Frequently Asked Questions
Is React Context bad for performance in React Native?
Not by itself. It causes problems when a frequently changing value is shared by many consumers. For stable values like theme and locale, it works well.
Do I need a state management library for every app?
No. Small apps do well with local state and a few contexts. Consider a store when many screens share fast-changing state or logic needs to run outside components.
Can I use useReducer with Context instead of a library?
Yes, for moderate needs. It organizes updates well, but all consumers of the state context still re-render on each change, so the same performance limits apply.
Where should I keep authentication state?
Context is a reasonable place for the signed-in user, since it changes rarely. Store sensitive tokens in secure storage, not in plain state or general-purpose storage.
Summary
Use Context for stable, app-wide values, split and memoize it, and avoid putting fast-changing data in it. Reach for selector-based external stores when many components share frequently updated state, and keep server state in a dedicated data layer. Profile on a real mid-range device and consult library documentation for version-specific behavior.