REACT RE-RENDER LOOP: TOO MANY RE-RENDERS OR MAXIMUM UPDATE DEPTH EXCEEDED
------------------------------------------------------------------------
A state update is triggering a render that triggers the same update again. The two error messages point at different halves of the problem — one means state is set during render, the other means an Effect is updating a value it also depends on — and neither names the component responsible.
CONFIDENCE : high
VERIFIED : 2026-08-07 (fresh, 0d old)
URL : https://knowbase.sh/k/react-infinite-render-loop
ERROR
------------------------------------------------------------------------
Too many re-renders. React limits the number of renders to prevent an infinite loop.
CODES: 185
PROBLEM
------------------------------------------------------------------------
The component renders endlessly until React throws, or the page freezes and the tab becomes unresponsive. The stack trace points into React internals rather than at application code, so the component actually causing the loop is not named. The same code often works in one place and loops in another, which makes it look intermittent.
ROOT CAUSE
------------------------------------------------------------------------
1. [primary] State is set during render rather than in response to an event
Almost always an event handler that is called instead of passed — onClick={handleClick()} runs on every render, and if it sets state the component re-renders and calls it again.
how to tell: The message is 'Too many re-renders'; look for a handler prop with parentheses, or a setState call in the component body
2. [primary] An Effect depends on a value it recreates every render
Objects, arrays and functions declared during render are new references each time. An Effect listing one in its dependency array runs after every commit, and if that Effect sets state the cycle never ends.
how to tell: The message is 'Maximum update depth exceeded' and the dependency array contains an object, array or inline function rather than only primitives
3. [common] An Effect updates state that is in its own dependency array
The Effect writes the very value that retriggers it. Even with a correct dependency list this is a closed loop by construction.
how to tell: The same identifier appears both in the dependency array and as the target of a setter inside the Effect body
4. [common] The dependency array is missing entirely
useEffect with no second argument runs after every render. That is harmless until the Effect sets state, at which point every render schedules another.
how to tell: The useEffect call has one argument; adding [] stops the loop
5. [edge] Chained Effects that adjust state to trigger each other
Several Effects each derive one piece of state from another. Each hop is a separate render pass, and a cycle anywhere in the chain becomes an infinite one.
how to tell: Multiple Effects exist whose only job is to set state derived from other state, and removing one breaks the loop
SOLUTION
------------------------------------------------------------------------
1. Read which of the two messages you have. They are different bugs: 'Too many re-renders' means state is being set during render; 'Maximum update depth exceeded' means an Effect is looping.
2. For a render-phase update, pass the handler rather than calling it. This single mistake accounts for most occurrences.
// 🔴 calls handleClick during every render
// ✅ passes the function, called only on click
// ✅ when arguments are needed
3. For an Effect loop, make the dependencies primitives. Depend on the fields you actually use rather than on the object that contains them, so a new reference with identical contents does not retrigger the Effect.
// 🔴 options is a new object every render
const options = { roomId, serverUrl };
useEffect(() => connect(options), [options]);
// ✅ depend on the primitive values instead
useEffect(() => {
const options = { roomId, serverUrl };
connect(options);
}, [roomId, serverUrl]);
note: Moving the object construction inside the Effect is usually better than wrapping it in useMemo — it removes the dependency rather than caching it.
4. If the Effect must update state derived from its own dependency, use a functional update so the current value is not a dependency at all.
// 🔴 count is a dependency, so the Effect retriggers itself
useEffect(() => { setCount(count + 1); }, [count]);
// ✅ no dependency on the current value
useEffect(() => { setCount((c) => c + 1); }, []);
5. Ask whether the Effect is needed at all. State derived from other state or props should be computed during render, not synchronised by an Effect — deriving it removes both the extra render pass and the possibility of a loop.
// 🔴 an Effect that mirrors derived state
const [visible, setVisible] = useState([]);
useEffect(() => { setVisible(items.filter((i) => i.active)); }, [items]);
// ✅ derive it during render
const visible = items.filter((i) => i.active);
6. If the culprit is still unclear, comment out Effects one at a time until the loop stops. React does not name the offending component, so bisection is faster than reading.
VERIFY: The component renders a bounded number of times for one interaction, the console reports no re-render or update-depth error, and React DevTools' profiler shows the render count settling instead of climbing.
FALLBACK: Where the value genuinely must be an object and cannot move inside the Effect, stabilise its identity with useMemo and the same primitive dependencies — treating that as a last resort, since a memo that is wrong simply hides the loop until the inputs change.
APPLIES TO
------------------------------------------------------------------------
React: 16.8 and later (Applies wherever hooks are used; the update-depth limit is a React runtime guard.)
React DOM: 16.8 and later
runtimes: browser, React Native
NOT APPLICABLE TO
------------------------------------------------------------------------
- Slow rendering without an error, which is a performance problem rather than a loop
- Hydration mismatches, where server and client output differ on the first render only
- Infinite loops in plain JavaScript outside a component, which never reach React's guard
- Effects that run twice in development, which is React Strict Mode behaviour and not a loop
EVIDENCE
------------------------------------------------------------------------
1. useState — I'm getting an error: Too many re-renders
https://react.dev/reference/react/useState
Meta Open Source | official-docs | read 2026-08-07
supports: That this error means state is being set unconditionally during render, and the render/set-state cycle that follows — including the event-handler mistake that most often causes it.
2. useEffect — Removing unnecessary object dependencies
https://react.dev/reference/react/useEffect
Meta Open Source | official-docs | read 2026-08-07
supports: That an object or function created during render is a new value on every render, so an Effect depending on it re-runs after every commit.
3. You Might Not Need an Effect
https://react.dev/learn/you-might-not-need-an-effect
Meta Open Source | official-docs | read 2026-08-07
supports: That an Effect which updates state restarts the render cycle from the beginning, and that data derived from props or state should be computed during render instead of synchronised by an Effect.
CONFIDENCE
------------------------------------------------------------------------
high — Both error conditions, the reference-identity rule behind Effect loops, and the guidance to derive rather than synchronise state are quoted from React's own reference and learning documentation. The bisection step is a debugging technique rather than documented procedure, and the split between the two messages is inferred from where each is documented rather than from a single sentence contrasting them.
------------------------------------------------------------------------
knowbase 0.1.0 — CC-BY-4.0