Maximum update depth exceeded (and its cousin Too many re-renders) means a component calls setState during render, triggering another render, which calls setState again — forever. Let's find the loop.
The Error
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
Cause 1: Calling a Function Instead of Passing It
The number one mistake — invoking the handler during render instead of passing a reference:
// ❌ Runs setCount on every render → infinite loop
<button onClick={setCount(count + 1)}>Add</button>
// ✅ Pass a function reference
<button onClick={() => setCount(count + 1)}>Add</button>Cause 2: setState Directly in the Component Body
// ❌ Updates state during render
function Profile({ user }) {
const [name, setName] = useState("");
setName(user.name); // re-render → setName → re-render ...
return <h1>{name}</h1>;
}
// ✅ Derive it, or sync inside useEffect
function Profile({ user }) {
return <h1>{user.name}</h1>;
}Cause 3: useEffect Without (or With a Bad) Dependency Array
An effect that sets state but depends on a value it also changes will loop. Use a primitive, stable dependency:
// ❌ No dependency array → runs after every render
useEffect(() => {
setData(transform(items));
});
// ✅ Only re-run when items actually change
useEffect(() => {
setData(transform(items));
}, [items]);Cause 4: A New Object/Array as a Dependency
Objects and arrays are compared by reference. A literal created in render is a new reference every time, so the effect runs every time. Memoize it with useMemo, or depend on its primitive fields instead.
How to Debug Fast
Comment out setState calls one by one until the loop stops — the last one you removed is your culprit. Then check: is it called in render, in an unguarded effect, or with an unstable dependency?
