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

6

69

Hi I have been stuck in a React Function useState. I just want to learn hooks and useState, but I can not have any progress even struggling too much to find a solution. Here is my full react function:

import React, { useState } from 'react';
import './MainPart.css';

function MainPart(props) {
  const [orderData_, setOrderData_] = useState(props.orderData);

  let topicData_ = props.topicData;
  let titleData_ = props.titleData;
  let infoData_ = props.infoData;

  return (
    <div className='MainPart'>
      <div className='mainWindow'>{getPics(orderData_)}</div>
      <div className='information'>
        <div className='moreNewsDivs'>
          <div className='moreNewsDiv1'>
            <h4>MORE NEWS</h4>
          </div>
          <div className='moreNewsDiv2'>
            <button
              className='previous-round'
              onClick={setOrderData_(previous(orderData_))}
            >
              &#8249;
            </button>
            &nbsp;&nbsp; &nbsp;&nbsp;
            <button href='/#' className='next-round'>
              &#8250;
            </button>
          </div>
        </div>
        <hr />
        <div className='topicDiv'>
          <h5 className='topicData'>{topicData_}</h5>
          <h5 className='titleData'>{titleData_}</h5>
          <h6 className='infoData'>{infoData_}</h6>
        </div>
      </div>
    </div>
  );
}

function previous(orderData_) {
  let newOrderData;

  if (orderData_ === 3) {
    newOrderData = 2;
    console.log(newOrderData);
    return newOrderData;
  } else if (orderData_ === 1) {
    newOrderData = 3;
    console.log(newOrderData);
    return newOrderData;
  } else {
    newOrderData = 1;
    console.log(newOrderData);
    return newOrderData;
  }
}

function next(orderData_) {
  let newOrderData;

  if (orderData_ === 3) {
    newOrderData = 1;
  } else if (orderData_ === 2) {
    newOrderData = 3;
  } else {
    newOrderData = 2;
  }
  return newOrderData;
}

const getPics = picOrder => {
  if (picOrder === 1) {
    return (
      <img
        src={require('../assets/desktopLarge/mainImage.png')}
        className='MainImage'
        alt=''
        id='mainImage'
      />
    );
  } else if (picOrder === 2) {
    return (
      <img
        src={require('../assets/desktopLarge/bridge.png')}
        className='MainImage'
        alt=''
        id='mainImage'
      />
    );
  } else {
    return (
      <img
        src={require('../assets/desktopLarge/forest.png')}
        className='MainImage'
        alt=''
        id='mainImage'
      />
    );
  }
};

export default MainPart;

I am getting an error while using useState. Even loading the page fresh and not pressed to anything my buttons onClick event listener activated and As I mentioned before at the topic My Error:

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

Disassociate answered 12/12, 2019 at 12:10 Comment(0)
P
185

The problem can be found in your onClick prop:

<button className="previous-round" onClick={setOrderData_(previous(orderData_))}>&#8249;</button>
                                            ^

Everything between the curly braces gets evaluated immediately. This causes the setOrderData_ function to be called in every render loop.

By wrapping the function with an arrow function, the evaluated code will result in a function that can be called whenever the user clicks on the button.

<button className="previous-round" onClick={() => setOrderData_(previous(orderData_))}
>&#8249;</button>

You can find more information about JSX and expressions in the official docs https://reactjs.org/docs/introducing-jsx.html#embedding-expressions-in-jsx

Infinite re-render loop

The reason for the infinite loop is because something (most likely setState) in the event callback is triggering a re-render. This will call the event callback again and causes React to stop and throw the 'Too many re-renders.' error.

Technical explanation

To better understand the reason why JSX works this way see the code below. JSX is actually being compiled to Javascript and every prop will be passed to a function in an Object. With this knowledge, you will see that handleEvent() is being called immediately in the last example.

// Simple example
// JSX: <button>click me</button>
// JS:  createElement('button', { children: 'click me' })
createElement("button", { children: "click me" });

// Correct event callback
// JSX: <button onClick={handleClick}>click me</button>
// JS:  createElement('button', { onClick: handleClick, children: 'click me' })
createElement("button", { onClick: handleClick, children: "click me" });

// Wrong event callback
// JSX: <button onClick={handleClick()}>click me</button>
// JS:  createElement('button', { onClick: handleClick(), children: 'click me' })
createElement("button", { onClick: handleClick(), children: "click me" });
Pandemic answered 12/12, 2019 at 12:18 Comment(4)
@christiaan, your answer is helpful but Why is that so? Any documentation where I can get the concept behind it? Many ThanksPrimo
@ZainUlAbideen here you go: reactjs.org/docs/…Pandemic
I still don't get it. the line clearly says that, on click do something. How could this be rendered infinitely, when it is only triggered by the click? React is still more magic to me than scienceExoergic
If you understand what JSX compiles to it might be easier to understand. JSX actually compiles to function calls on build. Check out this codesandbox where you can see why this is being called immediately. The infinitely part is caused by calling setState in the event handler causing a re-render and calling the onClick handler again.Pandemic
T
17

Just replace your button with the one below

<button className="previous-round" onClick={() => setOrderData_(previous(orderData_))}>&#8249;</button>

This happens because onClick function if used without an anonymous functions gets called immediately and that setOrderData again re renders it causing an infinite loop. So its better to use anonymous functions.

Hope it helps. feel free for doubts.

Thing answered 12/12, 2019 at 12:22 Comment(2)
still throwing the same error. Even I tried putting my logic in that anonymous func, but still the same.Zarla
could you clarify more what is happening internally so that we need to use an anonymous functionPierro
U
7

When I go through your code, I found something.

Onclick function needs to be arrow function. Onclick is an event and you are just calling a function inside onclick directly. This leads to too many re-renders because you are setting state directly inside the return. That does not work.

Calling setState here makes your component a contender for producing infinite loops. render should remain pure and be used to conditionally switch between JSX fragments/child components based on state or props. Callbacks in render can be used to update state and then re-render based on the change This above line taken from link here: https://itnext.io/react-setstate-usage-and-gotchas-ac10b4e03d60

Upbraiding answered 12/12, 2019 at 12:19 Comment(1)
Thanks mate . After working too much I couldnt see such simple mistakes. It worked.Disassociate
P
1

Just use Arrow (=>) function:

<button className="previous-round" onClick={() => setOrderData_(previous(orderData_))}>
&#8249;
</button>
Perfumery answered 2/12, 2020 at 10:56 Comment(0)
R
0

use

            <button
              className='previous-round'
              onClick={() => setOrderData_(previous(orderData_))}
            >
              &#8249;
            </button>

This worked for me...

Randeerandel answered 25/2, 2021 at 19:56 Comment(0)
R
-1

Inside onclick function, adding ()=> worked for me.

Robbi answered 21/11, 2023 at 12:8 Comment(1)
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewClathrate

© 2022 - 2024 — McMap. All rights reserved.