Understanding and fixing the "too many re-renders" error in React: A short guide

Understanding and fixing the "too many re-renders" error in React: A short guide
Photo by Tine Ivanič / Unsplash

If you have ever worked in React, there is a good chance that you encountered this error:

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

A simple button counter component will be used to understand what is happening behind this error.

Clicking on the button should increase a count by 1 and display it:

import { FC, useState } from 'react'

const Counter: FC = () => {
  const [count, setCount] = useState(0)

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
    </div>
  )
}

export default Counter

Everything works fine there, but if on click event handler is changed from

onClick={() => setCount(count + 1)}

to:

onClick={setCount(count + 1)}

Then you get the error mentioned before which causes the infinite loop.

But why?

First Class Functions

JavaScript has integrated the concept of first-class functions.

This means that functions can be treated as values, passed as an argument, and used as the return value from another function.

In the first example where everything was working correctly, an arrow function () => setCount(count + 1) was passed to the onClick React prop.

When the button is clicked, that arrow function is called and a state update will be triggered, which means the component will re-render.

Everything works fine.

Now to understand the bad example setCount(count + 1), and what is going on in the background, let’s first see how this looks in simple JavaScript terms:

const setValue = n => n
const onClick = () => setValue(1)
const onClickNew = setValue(1)

console.log(onClick) // () => setValue(1)
console.log(onClickNew) // 1

In this example, you can see that the arrow function is assigned as a value to the onClick variable.

On the other hand, the onClickNew variable value results from the execution of the setValue function, which means that the function setValue is called, the value is returned and assigned to the onClickNew function.

These things happen also in the React example above, but since React works differently it throws an error.

If you use onClick={setCount(count + 1)}, you are facing 2 problems:

  • You are trying to set the return value of setCount to the onClick prop, but setCount doesn’t return any value
  • You are updating the state during the render phase of Counter React component causing the infinite loop

Why an infinite loop?

Again, to understand this let’s show how to React behavior of updating the state looks in vanilla JavaScript:

function render() {
  render()
}
render()

It doesn’t take much time to recognize that this is recursion.

Infinite one.

So to explain what happens in React when you try to update the state during the component render phase:

  • The component renders and line onClick={setCount(count + 1)} is executed
  • setCount causes a state update
  • State update causes a component to rerender
  • The component renders and line onClick={setCount(count + 1)} is executed
  • setCount causes a state update
  • State update causes a component to rerender

The cycle goes on and on, in the infinite loop.

Conclusion

Although this is a beginner’s mistake and an easily fixable one, it is helpful to know why it produces this error.

If you enjoy this one, you can read a bit more about React rerenders and hooks behavior here