How to fix Context value changed between renders, this will cause issues in React

ReactINTERMEDIATEMEDIUMUpdated Dec 9, 2025· 106 views

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>
  );
}
  1. Wrap the context value object with useMemo
  2. Memoize all functions with useCallback
  3. Use useState or useReducer for state management
  4. Split context into separate providers if needed
react — error
// ❌ 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>
);
}
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

  1. Creating a new object literal directly in the value prop: value={{ user, login }}
  2. Defining functions inline in the Provider value without useCallback
  3. Not wrapping the context value object with useMemo
  4. Creating new array or object instances on each render
  5. Parent component re-renders causing Provider to receive new value reference
  6. Mixing data and functions in the same context object without proper memoization

How to fix "Context value changed between renders, this will cause issues"

1Wrap the context value object with useMemo

The primary fix is to memoize the entire value object using useMemo to maintain stable object identity:

typescript
// ❌ 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.

2Memoize all functions with useCallback

Any function included in the context value must be wrapped with useCallback to maintain stable function identity:

typescript
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.

3Use useState or useReducer for state management

State updater functions from useState and useReducer are already stable and don't need memoization:

typescript
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>
  );
}
4Split context into separate providers if needed

For complex contexts, consider splitting data and functions into separate providers to minimize re-renders:

typescript
// 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>;
}
5Verify optimization with React DevTools Profiler

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

typescript
// 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:

typescript
// ❌ 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:

typescript
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.

Official resources & further reading