Avoid Unnecessary Re-renders with Context in React: Simple Fixes
To avoid unnecessary re-renders with
React Context, keep context values stable by memoizing them with useMemo and split context into smaller pieces if possible. This prevents all components consuming the context from re-rendering when unrelated parts of the context change.Why This Happens
React re-renders all components consuming a context whenever the context value changes. If you provide an object or array directly in the context provider without memoization, it creates a new reference on every render, causing all consumers to re-render even if the data they use did not change.
jsx
import React, { createContext, useState } from 'react'; const MyContext = createContext(); function MyProvider({ children }) { const [count, setCount] = useState(0); const value = { count, setCount }; // New object every render return <MyContext.Provider value={value}>{children}</MyContext.Provider>; } function Display() { const { count } = React.useContext(MyContext); console.log('Display rendered'); return <div>Count: {count}</div>; } function App() { return ( <MyProvider> <Display /> </MyProvider> ); } export default App;
Output
Display rendered
Display rendered
Display rendered
... (renders on every parent render even if count is unchanged)
The Fix
Use React.useMemo to memoize the context value so it only changes when its contents change. This keeps the reference stable and prevents unnecessary re-renders. Also, consider splitting context into smaller contexts if you have unrelated data.
jsx
import React, { createContext, useState, useMemo } from 'react'; const MyContext = createContext(); function MyProvider({ children }) { const [count, setCount] = useState(0); const value = useMemo(() => ({ count, setCount }), [count]); return <MyContext.Provider value={value}>{children}</MyContext.Provider>; } function Display() { const { count } = React.useContext(MyContext); console.log('Display rendered'); return <div>Count: {count}</div>; } function App() { return ( <MyProvider> <Display /> </MyProvider> ); } export default App;
Output
Display rendered
Display rendered (only when count changes)
Prevention
To avoid this issue in the future:
- Always memoize context values with
useMemooruseCallbackwhen passing objects or functions. - Split large context objects into smaller, focused contexts to reduce re-renders.
- Use React DevTools Profiler to spot unnecessary renders.
- Consider using selectors or custom hooks to consume only needed parts of context.
Related Errors
Similar issues include:
- Prop drilling causing re-renders: Fix by using context or memoization.
- Unstable callback functions: Fix by wrapping callbacks with
useCallback. - State updates causing full tree re-renders: Fix by splitting state or using memoized selectors.
Key Takeaways
Memoize context values with useMemo to keep references stable and avoid re-renders.
Split context into smaller parts to limit re-renders to only components that need updates.
Use React DevTools Profiler to identify unnecessary re-renders.
Wrap functions passed in context with useCallback to prevent new references.
Consume only needed context data with custom hooks or selectors.