错误:重新渲染次数过多。React限制重新渲染的次数以防止无限循环。

68

嗨,我一直卡在一个React函数 useState 中。我只是想学习hooks和useState,但即使努力寻找解决方案也没有任何进展。以下是我的完整React函数:

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;

使用useState时,我遇到了一个错误。即使刷新页面并且没有按下任何按钮,我的按钮的onClick事件监听器也会被激活,并且如我之前在主题中提到的那样,我的错误是:

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

6个回答

182

问题可以在您的 onClick 属性中找到:

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

花括号之间的所有内容将立即被评估。这会导致在每个渲染循环中都调用setOrderData_函数。

通过使用箭头函数将函数包装起来,评估的代码将得到一个可以在用户单击按钮时调用的函数。

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

您可以在官方文档https://reactjs.org/docs/introducing-jsx.html#embedding-expressions-in-jsx中找到有关JSX和表达式的更多信息。

无限重新渲染循环

无限循环的原因是因为事件回调中的某些内容(最有可能是setState)触发了重新渲染。这将再次调用事件回调,并导致React停止并抛出“太多次重新渲染”的错误。

技术解释

为了更好地理解JSX工作方式的原因,请参见下面的代码。JSX实际上被编译为JavaScript,并且每个prop都将传递给对象中的函数。有了这个知识,您将看到在最后一个示例中handleEvent()会立即被调用。

// 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" });

1
@christiaan,你的回答很有帮助,但是为什么会这样呢?有没有相关概念的文档可以提供参考?非常感谢。 - Zain Ul Abideen
2
@ZainUlAbideen 这是你要的链接:https://reactjs.org/docs/introducing-jsx.html#embedding-expressions-in-jsx - Chris
2
我还是不明白。这行代码明确地表示,在点击时执行某些操作。当它只被点击触发时,怎么可能会无限渲染呢?对我来说,React仍然比科学更神奇。 - MGLondon
1
如果你理解JSX编译成什么样子,那么它可能更容易理解。实际上,JSX编译成构建时的函数调用。查看这个codesandbox,你可以看到为什么会立即调用它。无限部分是由于在事件处理程序中调用setState引起重新渲染并再次调用onClick处理程序。 - Chris

16

只需将您的按钮替换为下面的按钮

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

这是因为如果使用onClick函数而没有使用匿名函数,它会立即被调用,然后setOrderData会重新呈现它,导致无限循环。因此最好使用匿名函数。

希望能帮到您。有疑问请随时提出。


仍然抛出相同的错误。即使我尝试将我的逻辑放在那个匿名函数中,但仍然是相同的错误。 - Mansuu....
你能否更明确地解释一下内部发生了什么,以至于我们需要使用匿名函数? - Sandeep Mukherjee

6
当我查看你的代码时,我发现了一些问题。
onclick函数需要是箭头函数。Onclick是一个事件,你只是直接在onclick内调用一个函数。这会导致过多的重新渲染,因为你直接在返回值中设置状态。这并不起作用。
在这里调用setState会使你的组件有可能产生无限循环。render应该保持纯净,并根据状态或属性基于条件切换JSX片段/子组件。回调函数可以在render中用于更新状态,然后根据更改重新呈现。
此以上内容来自于这个链接:https://itnext.io/react-setstate-usage-and-gotchas-ac10b4e03d60

1
谢谢伙计。工作太多后我看不到这么简单的错误了。它已经运行成功了。 - Serkan AKMAN

1

只需使用箭头(=>)函数:

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

0

使用

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

这对我有用...


-1
在onclick函数内,添加() =>对我起了作用。

这并没有回答问题。一旦你有足够的声望,你就可以评论任何帖子;相反,提供不需要提问者澄清的答案。- 来自审查 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接