3 ways to cause an infinite loop in React
Tags: react, javascript
Published: 2021-07-06 10:56:02

Have you spent some time trying to debug an infinite loop in React? Maybe you hung your browser a couple of times in the process. Or had one of these 👇

```
Uncaught Error: Too many re-renders.
React limits the number of renders
to prevent an infinite loop.
```

Here are 3 potential causes of the infinite loop in React.

---

## I. Updating the state inside the render

```jsx
function App() {
  const [count, setCount] = useState(0);

  setCount(1); // infinite loop

  return ...
}
```

If you update the state directly inside your render method or a body of a functional component, it will cause an infinite loop.

*State updates → triggers re-render → state updates → triggers re-render → ...*

### Fix  🎉

Do you want to update a state only one time when the component mounts? Use `useEffect` with an empty array as a dependency.

```jsx
function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(1);
  }, [])
  

  return ...
}
```

---

## II. Infinite loop in useEffect

```jsx
function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(count + 1) // infinite loop
  }, [count])

  return ...
}
```

If you keep updating a state inside `useEffect` with a property you update set as a dependency, it will cause an infinite loop.

*`count` updates → `useEffect` detects updated dependency → `count` updates → `useEffect` detects updated dependency → ...*

### Fix  🎉

If you want to update a state based on its previous value, use a [functional update](https://reactjs.org/docs/hooks-reference.html#functional-updates). This way, you can remove state property from the dependency list and prevent an infinite loop.

```jsx
function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(previousCount => previousCount + 1)
  }, [])

  return ...
}
```

---

## III. Incorrectly set event handlers

```jsx
export default function App() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={setCount(1)}>Submit</button> // infinite loop
  );
}
```

It is not the right way to set event handlers. You need to provide a **function** to the `onClick`, not **the result of the function execution**. By executing a function before setting a handler, you update a state inside the render, which causes an infinite loop.

*State updates → triggers re-render → state updates → triggers re-render → ...*

### Fix 🎉

Set a **function** to `onClick` event. It is a proper way to set event handlers. This way state will only update after a click of a button and won't cause an infinite loop.

```jsx
export default function App() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(1)}>Submit</button> // infinite loop
  );
}
```

---

## How to spot infinite loops

Every time you update a state, picture the sequence of events that will happen after the update. If without additional user interaction, this sequence leads you back to the same state update, you probably have an infinite loop.

---

Discuss on [Twitter](https://mobile.twitter.com/asidorenko_/status/1412361231223099392)