Why Context Causes Re-renders and How to Reduce Them

Context re-renders every component that reads it when the value changes. Learn why and how to reduce the work with useMemo and useCallback.

6 min read

Every component that reads a context re-renders when the provider receives a different value, even if only part of the value changed. The reason is that React compares the old and new values with Object.is, and an inline object or function gets a new identity on every render. You can reduce the extra work by stabilizing the value.

How context re-renders work

React re-renders all readers of a context when the provider receives a different value. It decides whether the value changed by comparing the previous and next values with Object.is, which checks identity for objects and functions rather than their contents.

App.jsxApp.jsx
<AuthContext value={{ user, login }}>
  <Page />
</AuthContext>

This inline object is a different object on every App render, even when user is unchanged. React treats that as a changed value and re-renders every component that reads AuthContext.

Why the value changes identity

The provider creates the value object during render. A new render runs the code again, so a fresh object and a fresh function appear even if the data they wrap stayed the same.

The login function is also recreated each render. A function is compared by identity too, so passing it inline contributes to the same problem.

Stabilize the value with useMemo

Wrap the object in useMemo so it only changes when its dependencies change. The provider then reuses the same object across renders when nothing important moved.

App.jsxApp.jsx
import { useMemo, useState } from "react";
 
export default function App() {
  const [user, setUser] = useState(null);
  const value = useMemo(() => ({ user, setUser }), [user]);
  return (
    <AuthContext value={value}>
      <Page />
    </AuthContext>
  );
}

Now the context value keeps the same identity until user changes, so readers skip re-renders that only repeat the same data.

Stabilize functions with useCallback

A function passed through the value has the same identity problem. Wrap it in useCallback so it stays the same object until its dependencies change.

App.jsxApp.jsx
import { useCallback, useMemo, useState } from "react";
 
export default function App() {
  const [user, setUser] = useState(null);
  const login = useCallback((response) => {
    setUser(response.user);
  }, []);
  const value = useMemo(() => ({ user, login }), [user, login]);
  return (
    <AuthContext value={value}>
      <Page />
    </AuthContext>
  );
}

The login function now keeps the same identity across renders, and value only changes when user or login changes. Keep the dependency array accurate. An empty array means the function never changes, which is fine only when it captures no changing values.

What memo cannot do

React.memo skips a re-render when a component receives the same props. It does not shield a component from a changed context value, because context flows outside the props channel. If a component calls useContext and the value changes, it re-renders regardless of memoization.

When to optimize

Do not memoize context values by habit. Measure the render problem first, then apply useMemo and useCallback only when a context value updates often and many readers do expensive work. For a theme that changes rarely, the plain inline object is fine.

What to learn next

Splitting one large context into smaller ones removes re-renders without memoization. See how to split React context by responsibility. To wire a value to state in the first place, see how to update context values from child components.

Rune AI

Rune AI

Key Insights

  • Context readers re-render when the value changes identity.
  • React compares values with Object.is, not deep equality.
  • Inline objects and functions change identity on every render.
  • useMemo and useCallback keep the value stable.
  • React.memo does not prevent context-driven re-renders.
RunePowered by Rune AI

Frequently Asked Questions

Does React.memo stop context re-renders?

No. A component that reads context re-renders when the context value changes, even if it is wrapped in React.memo. Memoization skips work for unchanged props, not changed context.

When does React decide a context value changed?

React compares the previous and next values with Object.is. A new object or function on every render counts as a change, which is why inline values re-render readers.

Should I always memoize context values?

No. Measure first. Small apps rarely need it, and premature memoization adds complexity. Apply it when a context value updates often and its readers are numerous or expensive.

Conclusion

Context re-renders every component that reads it when the provider passes a new value. Stabilize the value with useMemo and useCallback when that work is actually measured as a problem, and remember that React.memo does not shield context readers.