Skip to main content

How do you optimize working with context?

1) Split context into small pieces

Do not put "everything at once" into one Provider. Split by domain - then when one value changes, only its consumers re-render.

javascript
<UserProvider value={user}> <ThemeProvider value={theme}> <I18nProvider value={i18n}> <App /> </I18nProvider> </ThemeProvider> </UserProvider>

2) Memoize value

The main rule: the value reference should change only when its data changes.

javascript
function ThemeProvider({ children }) { const [theme, setTheme] = useState<'light'|'dark'>('light'); const value = useMemo(() => ({ theme, setTheme }), [theme]); // good return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>; }

An anti-example (every render creates a new object -> all consumers get triggered):

javascript
<ThemeContext.Provider value={{ theme, setTheme }}> {/* bad */}

3) Separate "changes often" from "changes rarely"

Keep fast-changing data separate from stable data.

javascript
const CounterValueContext = createContext<number>(0); const CounterActionsContext = createContext<{ inc: () => void }>({ inc: () => {} }); function CounterProvider({ children }) { const [count, setCount] = useState(0); const inc = useCallback(() => setCount(c => c + 1), []); return ( <CounterActionsContext.Provider value={{ inc }}> <CounterValueContext.Provider value={count}> {children} </CounterValueContext.Provider> </CounterActionsContext.Provider> ); }

Now, changing count does not change the reference to actions, and consumers of the actions do not re-render.

4) Keep the Provider as close to consumers as possible

Place the Provider around the part of the tree where the context is actually needed - fewer "subscribers" means fewer re-renders.

5) Use selectors (targeted reads)

Out of the box, React re-renders all consumers, but you can narrow updates with context-selector libraries:

  • use-context-selector
  • Zustand/Jotai/Redux Toolkit with useSelector

Example with use-context-selector:

javascript
import { createContext, useContextSelector } from 'use-context-selector'; const CartContext = createContext<{items: Item[]; total: number}>({items:[], total:0}); function TotalPrice() { const total = useContextSelector(CartContext, v => v.total); // re-renders only when total changes return <span>{total}</span>; }

6) Do not pass "freshly created" objects/arrays

If you need to hand out derived data - memoize it on the consumer side:

javascript
function ProductsList() { const { items } = useContext(ProductsContext); const visible = useMemo(() => items.filter(p => p.visible), [items]); return <List items={visible} />; }

7) For global mutable state - useReducer

dispatch is stable by reference -> convenient to pass through context.

javascript
const StoreContext = createContext<{state: State; dispatch: React.Dispatch<Action>}>(null!); function StoreProvider({ children }) { const [state, dispatch] = useReducer(reducer, initial); const value = useMemo(() => ({ state, dispatch }), [state]); // dispatch is stable return <StoreContext.Provider value={value}>{children}</StoreContext.Provider>; }

8) External stores: useSyncExternalStore

If you need a thin subscription without re-rendering the whole context, move the data to an external store and subscribe:

javascript
const subscribe = (listener: () => void) => store.subscribe(listener); const getSnapshot = () => store.getState().pieceYouNeed; const usePiece = () => useSyncExternalStore(subscribe, getSnapshot);

This gives "selective" updates out of the box.

9) Stabilize functions in value

If value contains functions - wrap them in useCallback so the reference does not change for no reason.

javascript
const actions = useMemo(() => ({ save, remove }), [save, remove]); // where save/remove are useCallback

10) Context is for "config", not for "counters"

The less often a context changes, the better. Keep frequently updated pieces of state local (useState in components) or in an external store; context is for configuration, theming, permissions, locale, DI services, and so on.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.