React Context prevents prop drilling, but context updates can trigger unnecessary renders when state and actions are bundled together.
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 renderreturn (<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.
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.
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.
name still re-renders when the combined context object changes because color changed.React.memo appropriately to avoid parent-driven re-renders, but remember it does not block updates from a context the component consumes directly.