Context optimization
1) Split the context into meaningful parts
Do not put everything into one "fat" value.
- Before:
AppContext = { user, theme, notifications, cart } - After:
AuthContext,ThemeContext,UIContext,CartContext
This way, changes in the cart do not trigger re-renders in consumers of the theme, and so on.
2) Stabilize the Provider's value
A re-render of every subscriber fires whenever the reference changes. Give it stable references:
function AuthProvider({ children }) {
const [user, setUser] = useState<User | null>(null);
// stabilize the functions too
const login = useCallback((u: User) => setUser(u), []);
const logout = useCallback(() => setUser(null), []);
// value only changes when user changes
const value = useMemo(() => ({ user, login, logout }), [user]);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}Avoid:
<AuthContext.Provider value={{ user, login: (u)=>setUser(u) }}>(every re-render creates a new object/functions -> every subscriber updates).
3) Split "state" and "dispatch" into two contexts
dispatch/actions have a stable reference, so they can be handed out through a separate provider:
const CounterStateContext = createContext<number>(0);
const CounterDispatchContext = createContext<React.Dispatch<Action>>(() => {});
function CounterProvider({ children }) {
const [count, dispatch] = useReducer(reducer, 0);
return (
<CounterDispatchContext.Provider value={dispatch}>
<CounterStateContext.Provider value={count}>
{children}
</CounterStateContext.Provider>
</CounterDispatchContext.Provider>
);
}
// A subscriber that only needs to dispatch actions does not re-render when count changes4) Place the Provider as low in the tree as possible
The fewer subscribers inside the Provider's scope, the smaller the scale of updates.
- Wrap only the branches that need it, not the whole
<App />.
5) Use selectors (pinpoint subscriptions)
The use-context-selector library lets you subscribe to part of value, not the whole object.
import { createContext, useContextSelector } from 'use-context-selector';
const UserContext = createContext<{user: User, setUser:Fn}>(/* ... */);
function UserName() {
const name = useContextSelector(UserContext, v => v.user.name);
return <span>{name}</span>; // Re-renders only if the name changed
}This gives granular updates, similar to Redux.
6) Selector wrappers + React.memo
Sometimes it is convenient to make a small selector component that reads the context and hands down exactly the needed prop to a memoized component.
const PriceView = React.memo(function PriceView({ price }: { price: number }) {
return <span>{price}</span>;
});
function PriceFromContext() {
const price = useContext(CartContext).totalPrice; // only one field
return <PriceView price={price} />;
}React.memo "cuts off" unnecessary re-renders below it here if the prop did not change.
7) Do not store "fat" / frequently changing structures in context
- Put identifiers and simple data there.
- Keep heavy collections/caches in an external store (
useSyncExternalStore, Zustand/Jotai/Redux) or locally where they are used.
8) For frequent updates, prefer an external store
Context is not meant for high-frequency updates (timers, cursor position, input). For those:
- Zustand/Jotai/Redux + selectors
useSyncExternalStorewith your own subscription bus They update only the consumers that actually read the piece that changed.
9) Small habits that help a lot
- Do not pass new arrays/objects into
valuewithoutuseMemo. - Actions/callbacks go through
useCallback. - Do not compute something heavy on the fly inside
Provider- memoize it or move it out. - In TypeScript you can type contexts separately:
StateandActions- less temptation to "glue everything into one".
A short "cheat sheet" for choosing
- Rare updates + simple data -> Context (with points 1-6 above).
- Many different fields that change often -> split the contexts or move to a store with selectors.
- Very frequent updates (input, cursor, window size) -> an external store/subscription (
useSyncExternalStore, Zustand, and so on).
If you want, I can sketch a mini-refactor: send me your current Provider/consumers and I will convert it into a version with split contexts and selectors.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.