Home
React
Performance: Optimize Context
Daniel Nguyen
Daniel Nguyen
July 25, 2025
1 min

React Context prevents prop drilling, but context updates can trigger unnecessary renders when state and actions are bundled together.

1. Problem: Unstable Context Values

React compares context values using Object.is.

const CountContext = createContext(null);
function CountProvider({ children }) {
const [count, setCount] = useState(0);
const value = [count, setCount]; // New array on every render
return (
<CountContext.Provider value={value}>
{children}
</CountContext.Provider>
);
}

Whenever CountProvider renders, it creates a new array. React sees a different context value and notifies its consumers, even if count hasn’t changed.

2. Fix: Memoize the Context Value

function CountProvider({ children }) {
const [count, setCount] = useState(0);
const value = useMemo(
() => [count, setCount] as const,
[count]
);
return (
<CountContext.Provider value={value}>
{children}
</CountContext.Provider>
);
}

Now the context value remains stable while count stays unchanged.

Important: Consumers still re-render when count changes. Memoization prevents unnecessary context updates caused by creating a new array, but it doesn’t prevent renders caused by other factors.

3. Better Solution: Split State and Actions

Consider a footer that consumes both name and color, while another component only updates name.

If state and setters share one context, every consumer subscribes to the same context value.

Split them into separate contexts:

const FooterStateContext = createContext(null);
const FooterActionsContext = createContext(null);
function FooterProvider({ children }) {
const [name, setName] = useState("");
const [color, setColor] = useState("black");
const state = useMemo(
() => ({ name, color }),
[name, color]
);
const actions = useMemo(
() => ({ setName, setColor }),
[]
);
return (
<FooterStateContext.Provider value={state}>
<FooterActionsContext.Provider value={actions}>
{children}
</FooterActionsContext.Provider>
</FooterStateContext.Provider>
);
}

A component that only needs the setters can subscribe to FooterActionsContext without being notified when name or color changes.

function UpdateNameButton() {
const { setName } = useContext(FooterActionsContext);
return (
<button onClick={() => setName("Alex")}>
Update name
</button>
);
}

React state setter functions have stable identities, so the actions object remains stable as long as its dependencies remain unchanged.

4. Key Takeaways

  • Memoize context values to avoid unnecessary updates caused by new object or array references.
  • Split contexts by update frequency or responsibility so consumers subscribe only to the values they need.
  • Remember that context doesn’t track individual properties. A component reading only name still re-renders when the combined context object changes because color changed.
  • Use React.memo appropriately to avoid parent-driven re-renders, but remember it does not block updates from a context the component consumes directly.

Tags

#ReactPerformance

Share

Daniel Nguyen

Daniel Nguyen

Frontend Developer

Frontend developer specializing in React, Next.js, and JavaScript. Writing practical guides on modern web development at Dev98.

Expertise

React
Next.js
JavaScript
TypeScript
Python

Social Media

githublinkedinyoutubewebsite

Related Posts

React Performance
Performance: Windowing
August 06, 2025
1 min
Dev98

Dev98

React · Next.js · Web development