Blog
3 min read

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

(React useEffect explained)

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

  1. The stack trace names the component — start there.
  2. Look at every useEffect in it that calls a setter.
  3. For each, check its dependency array: is any dependency an object, array or function created during render?
  4. 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