# React: “Maximum update depth exceeded”

> React: Maximum update depth exceeded. An effect or handler sets state on every render. Fix the dependency array, object dependencies, or the state update.

Canonical: https://app369.com/fix/react-maximum-update-depth-exceeded/
Last updated: 2026-11-07

React logs or throws Maximum update depth exceeded. A state update causes a render, and that render triggers the same state update again, without end. React counts the nested updates and gives up at 50: with useEffect it logs a console warning and the page can freeze, with componentDidUpdate it throws. The cause is almost always a useEffect that sets state, with dependencies that change on every render.

**Short answer**

Find the useEffect (or componentDidUpdate) that calls a setter. If it has no dependency array, it runs after every render: add one. If a dependency is an object, array or function created during render, it is new every time: depend on a primitive value instead, move it inside the effect, or memoize it. If the state can be computed from props or other state, delete the effect and calculate it during render. In a class component, onClick={this.setState({ open: true })} runs during render and loops the same way: pass a function instead. (In a function component, onClick={setOpen(true)} gives "Too many re-renders" instead.)

## **Why the updates never stop, and the fix**

1. 01 ### **An effect sets state and has no dependency array**
  > “If you omit this argument, your Effect will re-run after every commit of the component.” [**React, useEffect**](https://react.dev/reference/react/useEffect) Without the second argument, the effect runs after every render. If it sets state, that state causes a render, which runs the effect again. Add the dependency array with only the values the effect really reads.```
// 🔴 runs after every render, sets new state each time, loops
useEffect(() => {
  setVisible(items.filter((i) => !i.done));
});

// ✅ runs only when items changes
useEffect(() => {
  setVisible(items.filter((i) => !i.done));
}, [items]);
```
2. 02 ### **A dependency is a new object or function each render**
  > “If some of your dependencies are objects or functions defined inside the component, there is a risk that they will cause the Effect to re-run more often than needed.” [**React, useEffect**](https://react.dev/reference/react/useEffect) An object, array or function written inside the component is a new value on every render, so an effect that depends on it runs every time. If that effect sets state, it loops. Depend on the primitive values you need, create the object inside the effect, or wrap it in useMemo or useCallback as a last resort. The same happens with a store selector that returns a new object each render (useStore((s) => ({ a: s.a, b: s.b })) in Zustand 5): select single values or use a shallow comparison.```
// 🔴 options is new each render
const options = { roomId };
useEffect(() => {
  connect(options);
  setAttempts((n) => n + 1);
}, [options]);

// ✅ depend on the value itself
useEffect(() => {
  connect({ roomId });
  setAttempts((n) => n + 1);
}, [roomId]);
```
3. 03 ### **The effect updates a value it also depends on**
  > “Your Effect is updating some state. That state leads to a re-render, which causes the Effect’s dependencies to change.” [**React, useEffect: My Effect keeps re-running in an infinite cycle**](https://react.dev/reference/react/useEffect) An effect that reads count and sets count, with count in its dependencies, triggers itself. Use the updater form so the effect does not need the value, or ask whether the effect is needed at all: values that can be computed from props or state belong in the render, not in an effect.```
// ✅ computed during render, no effect, no loop
const visibleTodos = todos.filter((t) => !t.done);
```
4. 04 ### **A setState during render or in componentDidUpdate** In a class component, onClick={this.setState({ open: true })} calls setState while rendering instead of on click; pass onClick={() => this.setState({ open: true })}. (In a function component, onClick={setOpen(true)} gives “Too many re-renders”, covered on the linked page.) setState in componentDidUpdate without comparing to the previous props or state loops the same way: wrap it in a condition.```
componentDidUpdate(prevProps) {
  if (prevProps.userId !== this.props.userId) {
    this.setState({ user: null });
  }
}
```

Checked against the vendor’s docs on November 7, 2026: [React: useEffect (dependencies and the infinite cycle troubleshooting section)](https://react.dev/reference/react/useEffect).

## **Rather hand it over?**

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

- [from $450**App Fix Ticket**One reproducible bug in an existing mobile or web app, diagnosed and fixed.**See scope and start**](https://app369.com/fixed-price/flutter-firebase-bug-fix/)
- [from $450**AI-Built App Rescue**A Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.****No issues worth fixing, no charge.**** If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.**See scope and start**](https://app369.com/fixed-price/ai-app-rescue/)

Related: [**React “Too many re-renders”**](https://app369.com/fix/react-too-many-re-renders/) · [**React “Invalid hook call”**](https://app369.com/fix/react-invalid-hook-call/) · [**Next.js hydration error**](https://app369.com/fix/nextjs-hydration-error/)

Questions

## **More on this error**

<details>

<summary>**What is the difference from “Too many re-renders”? **+</summary>

Too many re-renders means a setter ran during the render itself, so React stops before showing anything. Maximum update depth means updates keep coming from effects or lifecycle methods after each render. The fix is the same idea: find the state update that triggers itself and give it a condition.

</details>

<details>

<summary>**How do I find which effect is looping? **+</summary>

The stack trace usually names the component. In that component, look for effects that call a setter. React DevTools’ Profiler, or a console.log inside each effect, shows which one runs on every render.

</details>

<details>

<summary>**Can you fix it for me? **+</summary>

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

</details>

A different error?

## **Describe it and get a written quote**

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.

[**Describe your problem **→](https://app369.com/contact/) [**All fixes**](https://app369.com/fix/)