How to fix Context value changed between renders, this will cause issues in React
This warning occurs when a React Context Provider receives a new value object on every render, causing all consuming components to re-render even when the actual data hasn't changed. It's typically caused by creating objects or functions inline without memoization.
Quick fix
Fixes Context value changed between renders, this will cause issues
// ❌ BEFORE: New object on every render
function UserProvider({ children }) {
const [user, setUser] = useState(null);
const login = (userData) => {
setUser(userData);
};
// This creates a NEW object every render!
return (
<UserContext.Provider value={{ user, login }}>
{children}
</UserContext.Provider>
);
}
// ✅ AFTER: Stable object reference
function UserProvider({ children }) {
const [user, setUser] = useState(null);
const login = useCallback((userData) => {
setUser(userData);
}, []);
const value = useMemo(
() => ({ user, login }),
[user, login]
);
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
}- Wrap the context value object with useMemo
- Memoize all functions with useCallback
- Use useState or useReducer for state management
- Split context into separate providers if needed
// ❌ BEFORE: New object on every renderfunction UserProvider({ children }) {const [user, setUser] = useState(null);const login = (userData) => {setUser(userData);};// This creates a NEW object every render!return (<UserContext.Provider value={{ user, login }}>{children}</UserContext.Provider>);}// ✅ AFTER: Stable object referencefunction UserProvider({ children }) {const [user, setUser] = useState(null);const login = useCallback((userData) => {setUser(userData);}, []);const value = useMemo(() => ({ user, login }),[user, login]);return (<UserContext.Provider value={value}>{children}</UserContext.Provider>);}Context value changed between renders, this will cause issues
What this error means
This issue occurs when the `value` prop passed to a React Context Provider changes its reference identity on every render, even though the actual data inside may be the same. React uses strict equality (===) to check if the context value has changed, not deep comparison of the object's contents. When your component re-renders and creates a new object literal or function as the context value, React sees this as a completely different value. This triggers re-renders in all components that consume this context, regardless of whether they actually use the changed data. This is a common performance pitfall that can cause cascading re-renders throughout your component tree, especially in larger applications where many components subscribe to the same context. The issue becomes more severe when the Provider is located high in the component tree and re-renders frequently.
Typical symptoms
- All components using useContext re-render whenever the Provider's parent component re-renders
- Performance degradation with slow or laggy UI interactions
- React DevTools Profiler shows excessive component updates
- Components re-render even when their portion of context data hasn't changed
- Warnings or performance issues after wrapping components with React.memo but still seeing re-renders
Common causes
- Creating a new object literal directly in the value prop: value={{ user, login }}
- Defining functions inline in the Provider value without useCallback
- Not wrapping the context value object with useMemo
- Creating new array or object instances on each render
- Parent component re-renders causing Provider to receive new value reference
- Mixing data and functions in the same context object without proper memoization
How to fix "Context value changed between renders, this will cause issues"
The primary fix is to memoize the entire value object using useMemo to maintain stable object identity:
// ❌ BEFORE: New object on every render
function UserProvider({ children }) {
const [user, setUser] = useState(null);
const login = (userData) => {
setUser(userData);
};
// This creates a NEW object every render!
return (
<UserContext.Provider value={{ user, login }}>
{children}
</UserContext.Provider>
);
}
// ✅ AFTER: Stable object reference
function UserProvider({ children }) {
const [user, setUser] = useState(null);
const login = useCallback((userData) => {
setUser(userData);
}, []);
const value = useMemo(
() => ({ user, login }),
[user, login]
);
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
}The useMemo hook ensures the value object only gets recreated when its dependencies (user or login) actually change.
Any function included in the context value must be wrapped with useCallback to maintain stable function identity:
function AppProvider({ children }) {
const [state, setState] = useState(initialState);
// ✅ Memoized functions
const updateUser = useCallback((user) => {
setState(prev => ({ ...prev, user }));
}, []);
const logout = useCallback(() => {
setState(prev => ({ ...prev, user: null }));
}, []);
const refreshData = useCallback(async () => {
const data = await fetchData();
setState(prev => ({ ...prev, data }));
}, []);
const value = useMemo(
() => ({
state,
updateUser,
logout,
refreshData
}),
[state, updateUser, logout, refreshData]
);
return (
<AppContext.Provider value={value}>
{children}
</AppContext.Provider>
);
}Without useCallback, each function would be recreated on every render, changing the value object's identity.
State updater functions from useState and useReducer are already stable and don't need memoization:
function DataProvider({ children }) {
const [data, setData] = useState(null);
// setData is stable - no need for useCallback
const value = useMemo(
() => ({ data, setData }),
[data]
);
return (
<DataContext.Provider value={value}>
{children}
</DataContext.Provider>
);
}
// Or with useReducer
function StateProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
// dispatch is stable - no need for useCallback
const value = useMemo(
() => ({ state, dispatch }),
[state]
);
return (
<StateContext.Provider value={value}>
{children}
</StateContext.Provider>
);
}For complex contexts, consider splitting data and functions into separate providers to minimize re-renders:
// Data context - changes frequently
const UserDataContext = createContext(null);
// API context - functions that rarely change
const UserAPIContext = createContext(null);
function UserProvider({ children }) {
const [user, setUser] = useState(null);
// Functions are memoized once
const api = useMemo(() => ({
login: (userData) => setUser(userData),
logout: () => setUser(null),
updateProfile: (updates) => setUser(prev => ({ ...prev, ...updates }))
}), []);
return (
<UserDataContext.Provider value={user}>
<UserAPIContext.Provider value={api}>
{children}
</UserAPIContext.Provider>
</UserDataContext.Provider>
);
}
// Components only consuming API won't re-render when user data changes
function LogoutButton() {
const { logout } = useContext(UserAPIContext);
return <button onClick={logout}>Logout</button>;
}
// Components consuming data will re-render when user changes
function UserProfile() {
const user = useContext(UserDataContext);
return <div>{user?.name}</div>;
}Use React DevTools to confirm your memoization is working:
1. Open React DevTools and go to the Profiler tab
2. Start recording
3. Trigger actions that cause the Provider's parent to re-render
4. Stop recording and examine the flame graph
Look for:
- Consumer components should NOT re-render when parent re-renders (unless the context value actually changed)
- The Provider itself will still re-render (that's expected)
- Only components using changed portions of data should update
// You can also add console.logs temporarily to debug
function ExpensiveComponent() {
const { user } = useContext(UserContext);
console.log('ExpensiveComponent rendered'); // Should not log unnecessarily
return <div>{user?.name}</div>;
}Advanced notes
When NOT to optimize prematurely:
Don't add useMemo/useCallback everywhere by default. Start with simple code and only optimize when you observe actual performance issues. The React team recommends measuring first with the Profiler before optimizing.
Context + React.memo interaction:
Wrapping a component with React.memo will NOT prevent re-renders caused by context changes. If a component uses useContext, it will always re-render when that context value changes, regardless of memo. This is by design - context is an opt-in subscription.
Alternative patterns:
For very complex state management needs, consider:
- Zustand or Jotai for lightweight state management
- Redux Toolkit for application-wide state with more control over re-renders
- React Query/TanStack Query for server state management
- XState for complex state machines
Dependency array gotchas:
Be careful with the useMemo dependency array. If you include objects or arrays that are recreated on every render, your memoization won't work:
// ❌ Still creates new object every render
const value = useMemo(
() => ({ user, settings }),
[user, { theme: 'dark' }] // Object literal in deps!
);
// ✅ Extract stable values
const theme = 'dark';
const value = useMemo(
() => ({ user, settings: { theme } }),
[user, theme]
);TypeScript typing:
When splitting contexts, properly type them to avoid null checks:
const UserContext = createContext<User | null>(null);
function useUser() {
const context = useContext(UserContext);
if (context === null) {
throw new Error('useUser must be used within UserProvider');
}
return context;
}Frequently asked questions
- What does "Context value changed between renders, this will cause issues" mean?
- This issue occurs when the value prop passed to a React Context Provider changes its reference identity on every render, even though the actual data inside may be the same. React uses strict equality (===) to check if the context value has changed, not deep comparison of the object's contents. When your component re-renders and creates a new object literal or function as the context value, React sees this as a completely different value.
- How do I fix "Context value changed between renders, this will cause issues"?
- This warning occurs when a React Context Provider receives a new value object on every render, causing all consuming components to re-render even when the actual data hasn't changed. It's typically caused by creating objects or functions inline without memoization.
- What causes "Context value changed between renders, this will cause issues"?
- Creating a new object literal directly in the value prop: value={{ user, login }} Defining functions inline in the Provider value without useCallback Not wrapping the context value object with useMemo
- Is the "Context value changed between renders, this will cause issues" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.