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

Posted

技术标签:

【中文标题】“错误:重新渲染过多。React 限制了渲染次数以防止无限循环。”【英文标题】:"Error: Too many re-renders. React limits the number of renders to prevent an infinite loop." 【发布时间】:2020-04-05 19:50:51 【问题描述】:

嗨,我被困在一个 React 函数 useState 中。我只是想学习 hooks 和 useState,但即使努力寻找解决方案,我也无法取得任何进展。这是我的完整反应功能:

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 事件侦听器激活,正如我之前在主题我的错误中提到的那样:

“错误:重新渲染过多。React 将渲染次数限制为 防止无限循环。”

【问题讨论】:

【参考方案1】:

问题可以在你的onClick prop中找到:

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

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

通过用箭头函数包装函数,评估后的代码将生成一个函数,只要用户点击按钮,就可以调用该函数。

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

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

无限重新渲染循环

无限循环的原因是因为事件回调中的某些东西(很可能是setState)正在触发重新渲染。这将再次调用事件回调并导致 React 停止并抛出“Too many re-renders”。错误。

技术说明

为了更好地理解 JSX 以这种方式工作的原因,请参见下面的代码。 JSX 实际上被编译为 javascript,每个 prop 都将传递给 Object 中的一个函数。有了这些知识,您将看到在最后一个示例中立即调用了 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" );

【讨论】:

@christiaan,您的回答很有帮助,但为什么会这样?有什么文档可以让我了解它背后的概念吗?非常感谢 @ZainUlAbideen 给你:reactjs.org/docs/… 我还是不明白。这条线清楚地表明,点击做某事。当它仅由点击触发时,如何无限呈现? React 对我来说仍然比科学更神奇 如果你理解 JSX 编译成什么,它可能更容易理解。 JSX 实际上编译为构建时的函数调用。查看此codesandbox,您可以在其中了解为什么会立即调用它。无限部分是由在事件处理程序中调用 setState 导致重新渲染并再次调用 onClick 处理程序引起的。【参考方案2】:

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

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

发生这种情况是因为 onClick 函数如果在没有匿名函数的情况下使用会立即被调用,并且 setOrderData 再次重新渲染它导致无限循环。所以最好使用匿名函数。

希望对您有所帮助。如有疑问,请随意。

【讨论】:

仍然抛出同样的错误。即使我尝试将我的逻辑放在那个匿名函数中,但还是一样。 您能否进一步说明内部发生的情况,以便我们需要使用匿名函数【参考方案3】:

当我浏览你的代码时,我发现了一些东西。

Onclick 功能需要是箭头功能。 Onclick 是一个事件,您只是直接在 onclick 中调用一个函数。这会导致过多的重新渲染,因为您直接在返回中设置状态。那是行不通的。

在这里调用 setState 会使您的组件成为产生无限循环的竞争者。 render 应该保持纯净,并用于根据状态或道具有条件地在 JSX 片段/子组件之间切换。渲染中的回调可用于更新状态,然后根据更改重新渲染 上面这行取自此处的链接:https://itnext.io/react-setstate-usage-and-gotchas-ac10b4e03d60

【讨论】:

谢谢伙计。工作太多后,我看不到这么简单的错误。它奏效了。【参考方案4】:

只需使用箭头 (=&gt;) 功能:

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

【讨论】:

【参考方案5】:

使用

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

这对我有用...

【讨论】:

以上是关于“错误:重新渲染过多。React 限制了渲染次数以防止无限循环。”的主要内容,如果未能解决你的问题,请参考以下文章