reactseverity: workaround
#185

React Error #185: Maximum update depth exceeded

Maximum update depth exceeded

90% fixable~10 mindifficulty: intermediate

Verified against React 19 source (ReactFiberWorkLoop.js), GitHub issue #6895 (1.3k reactions), React docs: synchronizing-with-effects · Updated April 2026

> quick_fix

A component is calling setState inside a render or an un-dependency-guarded useEffect. Move the setState call inside an event handler, add the missing dependency, or gate it with a condition.

// ❌ Causes #185 (setState during render)
function Bad() {
  const [n, setN] = useState(0)
  setN(n + 1)  // runs on every render
  return <p>{n}</p>
}

// ✅ Fix: gate with useEffect + dependency
function Good({ userId }) {
  const [n, setN] = useState(0)
  useEffect(() => { setN(x => x + 1) }, [userId])
  return <p>{n}</p>
}

What causes this error

React limits any component to 50 consecutive synchronous re-renders to catch infinite loops. Error #185 means your component crossed that limit. Common patterns: setState during render, setState inside an un-dependency-guarded useEffect, or a reducer that dispatches a new action on every render.

> advertisementAdSense placeholder

How to fix it

  1. 01

    step 1

    Find the component name in the stack trace

    React usually points you at the component. Open it and look for setState calls outside event handlers.

  2. 02

    step 2

    Check every useEffect for its dependency array

    An empty array [] means run once. A missing array means run on every render. Any state you set in the effect must be gated by a dependency.

  3. 03

    step 3

    Use the updater form for state that depends on itself

    Instead of setN(n + 1), write setN(x => x + 1). This prevents a stale closure that can cause loops.

    // ❌ Stale closure — may loop
    useEffect(() => { setN(n + 1) }, [])
    
    // ✅ Updater — safe
    useEffect(() => { setN(x => x + 1) }, [])
  4. 04

    step 4

    If rendering a list, confirm keys are stable

    Unstable keys cause React to unmount+remount children, which re-fires their useEffect chains.

Why #185 happens at the runtime level

React's reconciler enforces a 50-deep render guard inside ReactFiberWorkLoop.js (NESTED_UPDATE_LIMIT). Each call to setState during render or inside an effect that mutates a dependency it also reads schedules a new Fiber work-in-progress tree. The scheduler drains its work queue synchronously when these updates originate from render or layout phases, so a setState that always changes its referenced state spins the work loop. After 50 cycles without a yield, React aborts with invariant #185 to prevent locking the main thread indefinitely.

Common debug mistakes for #185

  • Adding a state.value !== newValue guard before setState but writing setN({ ...obj }), object identity changes every render even if contents are equal, and React.memo/useEffect see it as new.
  • Switching from useState to useReducer assuming the loop will stop, reducers run inside the same render cycle and loop identically if the dispatch is unconditional.
  • Adding [] as the dependency array to silence the warning, the effect now runs once but the underlying setState-during-render bug elsewhere still triggers #185 on subsequent renders.
  • Wrapping setState in setTimeout(fn, 0), masks the symptom in dev, but React 18+ batches the macrotask and the invariant still fires under concurrent rendering.
  • Using getDerivedStateFromProps to mirror props into state, the pattern is itself an anti-pattern and a top source of #185 in class components.

When #185 signals a deeper problem

Persistent #185 usually points to a state model that duplicates source-of-truth data. When the same value lives in props, parent state, and child state simultaneously, any synchronisation effect becomes a circular dependency. The architectural fix is to lift state to its single owner and derive everything else with useMemo or plain expressions. If you find yourself writing useEffect to sync two state slots, the slots are redundant. This pattern compounds in form libraries, theme providers, and route-aware components where multiple layers each cache the same fact.

Editor's take

This one surfaces at the worst moment in a small-to-mid-size startup: after a seemingly unrelated refactor lands in a release branch, QA misses it because the triggering condition requires a specific combination of props that only appears in production data, and it blows up during the first morning after deploy when real users hit the edge case. The on-call engineer sees a white screen, React DevTools is not available in prod, and the minified stack trace points into the React internals at ReactFiberWorkLoop.js rather than anything recognisable in the application code. Source maps either weren't uploaded or are one commit behind.

Diagnosing Error #185 cleanly is a reliable proxy for how well an engineer understands the React render contract. A developer in their first few months will immediately reach for a useEffect cleanup function or wrap setState in a setTimeout — both of which suppress the symptom without addressing the loop. The engineer who actually fixes it properly will draw a dependency graph, notice that a derived value is being round-tripped through useState when useMemo suffices, and remove the synchronisation effect entirely. That second engineer is operating at a qualitatively different level of React comprehension, and it shows clearly during code review.

When this error fires in production, it rarely arrives alone. You will often find React Warning: Cannot update a component from inside the function body of a different component in the same session, which signals the loop is crossing component boundaries. If you are using React Query or SWR, watch for stale-while-revalidate cycles triggering redundant refetches — queryClient.invalidateQueries called inside an onSuccess handler that itself triggers the query is a classic co-offender. Redux users frequently see dispatch inside a selector memo, which pairs with Error #185 and produces cascading store churn.

By Bikram Nath · Curator · Updated April 2026

Frequently asked questions

What is error #185 in React?

Maximum update depth exceeded — a component triggered more than 50 synchronous re-renders. Almost always an unintentional setState loop.

How do I find which useEffect is the problem?

Temporarily disable your useEffects one at a time. The loop will stop when you disable the culprit.

Can React Strict Mode cause #185?

No. Strict Mode doubles renders in development but does not re-trigger infinite loops.

disclosure:Errordex runs AdSense, has zero third-party affiliate or sponsored links, and occasionally links to the editor’s own paid digital products (clearly labelled). Every fix is cross-referenced against the official sources listed in the “sources” sidebar before it ships. If a fix here didn’t work for you, please email so we can update the page.