function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = useCallback(u => setUser(u), []);
const value = useMemo(() => ({ user, login }), [user, login]);
return <AuthContext value={value}>{children}</AuthContext>;
}
Every component that reads a context re-renders when the provider's value changes. React compares the old and new value with Object.is. memo does not stop this: a memoized component that calls useContext still re-renders when the context changes.
Pitfall 1: a new object every render. For example, value={{ user, login }} creates a new object each time. So any re-render of the provider's component re-renders all consumers, even when nothing meaningful changed. Fix it by wrapping the object in useMemo and the callbacks in useCallback.
Pitfall 2: one big context. Unrelated or fast-changing values in one context cause extra renders. A consumer that only needs login still re-renders when user changes.
Fixes for pitfall 2:
- Split it into separate contexts, for example one for state and one for dispatch.
- Move rapidly changing state into a store that supports selectors, such as Zustand or Redux. A selector lets a component subscribe to only the part it uses.
- All consumers re-render when the context value changes.
memodoes not block re-renders caused by context.- A new object or function value each render causes needless updates, so memoize it.
- Split contexts by concern or by how often they update.
- Use a store with selectors for shared state that changes often.