React "Maximum Update Depth Exceeded": Causes and Fixes
React stops your app when a component keeps updating itself in a loop. The usual causes — setState in useEffect with a dependency that changes every render, objects and functions in dependency arrays, calling a setter during render — and how to fix each.
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.
Or, as a warning:
Warning: Maximum update depth exceeded. This can happen when a component calls
setState inside useEffect, but useEffect either doesn't have a dependency array,
or one of the dependencies changes on every render.
Your component is stuck in a loop: it updates state → re-renders → updates state again → re-renders… React stops it before the browser freezes.
It's a close cousin of "Too many re-renders", which happens when the loop is directly in the render itself.
Cause 1: useEffect with no dependency array
useEffect(() => {
setCount(count + 1)
}) // ❌ runs after every render, and causes another render
Without a dependency array, the effect runs after every render. Add the array:
useEffect(() => {
setCount(c => c + 1)
}, []) // runs once after the first render
Cause 2: a dependency that's new every render
This is the sneaky one:
function Results({ query }) {
const filters = { query, limit: 20 } // a NEW object every render
useEffect(() => {
fetchResults(filters).then(setResults) // setResults triggers a render
}, [filters]) // ❌ "changed" every time
}
React compares dependencies by identity. A newly created object, array or function is never equal to the previous one, so the effect runs every render, sets state, renders again…
Fixes:
// depend on the primitive values instead
useEffect(() => {
fetchResults({ query, limit: 20 }).then(setResults)
}, [query])
// or keep the object stable
const filters = useMemo(() => ({ query, limit: 20 }), [query])
Functions defined in the component have the same problem — move them inside the effect, outside the component, or wrap them in useCallback. (useMemo vs useCallback)
Cause 3: setting state that the effect depends on
useEffect(() => {
setItems([...items, newItem])
}, [items]) // ❌ updating items re-triggers the effect
Use the functional update form and drop the dependency:
useEffect(() => {
setItems(prev => [...prev, newItem])
}, [newItem])
Cause 4: syncing state from props on every render
useEffect(() => {
setFullName(`${first} ${last}`)
}, [first, last, fullName]) // fullName in deps keeps it looping
You often don't need state for this at all — calculate it during render:
const fullName = `${first} ${last}`
Storing values you can derive is a common source of loops and bugs. (React useState explained)
Cause 5: parent and child updating each other
A child calls onChange in an effect whenever a prop changes, the parent updates state, passing a new prop, which triggers the child's effect again. Only call the parent's callback in response to a user action (click, input), not in an effect that reacts to props.
How to find the loop
- The stack trace names the component — start there.
- Look at every
useEffectin it that calls a setter. - For each, check its dependency array: is any dependency an object, array or function created during render?
- Temporarily add
console.log('effect ran')— if it floods the console, you've found it. (Browser developer tools)
React's ESLint rules (react-hooks/exhaustive-deps) warn about many of these as you type.
Is the effect needed at all?
Many loops come from effects that shouldn't exist: deriving values, transforming data for display, or responding to events. Data fetching is often better handled by a library. (TanStack Query vs useEffect)
EasySpawn runs your React app next to Claude Code on a server with a live preview — paste the error and it can trace the loop through your components. See how it works or join the waitlist.
Related: React "Too Many Re-renders" · React useEffect Explained · useMemo vs useCallback · React useState Explained
Keep reading
"ReferenceError: window is not defined" in Next.js: How to Fix It
window, document and localStorage only exist in the browser. Next.js renders components on the server first, where they don't exist. Why it happens, and the fixes: useEffect, 'use client', typeof checks, dynamic imports with ssr: false, and libraries that touch window on import.
"Unexpected Token < in JSON at Position 0": What It Means and How to Fix It
Your code expected JSON and got HTML — almost always an error page or your app's index.html. Why it happens (wrong URL, 404, server error, SPA fallback, login redirect), how to see what the server actually sent, and how to parse responses safely.