ReactJS:最大更新深度超出错误


176

我试图在ReactJS中切换组件的状态,但出现错误:

超过最大更新深度。当组件重复调用componentWillUpdate或componentDidUpdate内部的setState时,可能会发生这种情况。React限制了嵌套更新的数量,以防止无限循环。

我在代码中看不到无限循环,有人可以帮忙吗?

ReactJS组件代码:

import React, { Component } from 'react';
import styled from 'styled-components';

class Item extends React.Component {
    constructor(props) {
        super(props);     
        this.toggle= this.toggle.bind(this);
        this.state = {
            details: false
        } 
    }  
    toggle(){
        const currentState = this.state.details;
        this.setState({ details: !currentState }); 
    }

    render() {
        return (
            <tr className="Item"> 
                <td>{this.props.config.server}</td>      
                <td>{this.props.config.verbose}</td> 
                <td>{this.props.config.type}</td>
                <td className={this.state.details ? "visible" : "hidden"}>PLACEHOLDER MORE INFO</td>
                {<td><span onClick={this.toggle()}>Details</span></td>}
            </tr>
    )}
}

export default Item;

35
更改this.toggle()this.toggle{()=> this.toggle()}
学习者

8
另一个改进(尽管与您的问题无关):toggle(){...}变为,toggle = () => {...}因此您无需这样做bind
Berry M.

谢谢@learner。你也帮了我 您能否解释一下解决方案背后的原因。两者之间有什么区别?
Shamim

2
@Shamim调用现有函数与将引用传递给函数之间的区别。了解我们正在编写的代码将在用户执行某些操作时显示和触发,而不是在用户加载页面后立即触发的代码,这对我们很有帮助。reactjs.org/docs/faq-functions.html
DisplayName

Answers:


273

那是因为您在render方法中调用了toggle,这将导致重新渲染,并且toggle将再次调用并再次重新渲染,依此类推

这行代码

{<td><span onClick={this.toggle()}>Details</span></td>}

您需要onClick参考this.toggle不调用它

解决此问题,请执行此操作

{<td><span onClick={this.toggle}>Details</span></td>}

8
我正面临类似的情况,但是我需要传递一个参数来切换,这如何实现?
Niveditha Karmegam,

58
@NivedithaKarmegam待办事项onClick={(param) => this.toggle(param)}。这不会立即触发(并重新渲染)。这是一个回调定义(箭头功能)。
Fabian Picone

15
@FabianPicone尝试了您的方法,但是当我console.log显示它已将“ param”作为事件传递时,实际上应该执行onClick={() => this.toggle(param)}
iWillGetBetter

6
@iWillGetBetter是的,onClick中的第一个参数是click事件。如果您需要其他参数,也可以通过它onClick={(event) => this.toggle(event, myParam)}
Fabian Picone

1
我有此功能closeEditModal = () => this.setState({openEditModal: false});如何在渲染中调用它?
努克斯

31

调用函数时,您应该传递事件对象:

{<td><span onClick={(e) => this.toggle(e)}>Details</span></td>}

如果您不需要处理onClick事件,也可以输入:

{<td><span onClick={(e) => this.toggle()}>Details</span></td>}

现在,您还可以在函数中添加参数。


2
如果未指定任何内容,则将自动发送事件对象。只需在调用的函数中包含一个输入参数即可。
杰夫·平克斯顿

2
{<td><span onClick={() => this.toggle(whateverParameter)}>Details</span></td>}为我
帮忙

22

首先,忘记反应:
这与反应无关,让我们了解Java Script的基本概念。例如,您已经用Java脚本(名称为A)编写了以下函数。

function a() {

};

Q.1)如何调用我们定义的函数?
答:a();

Q.2)如何传递函数的引用,以便我们可以稍后调用它?
答:让我们开心吧= a;

现在来问您的问题,您已经在函数名称中使用了括号,这意味着在呈现以下语句时将调用该函数。

<td><span onClick={this.toggle()}>Details</span></td>

那么如何纠正呢?
简单!!只需删除括号即可。通过这种方式,您已将该函数的引用提供给onClick事件。仅当单击组件时,它才会回调您的函数。

 <td><span onClick={this.toggle}>Details</span></td>

有一个建议可以做出反应:
避免像别人在答案中建议的那样使用内联函数,否则可能会导致性能问题。避免遵循以下代码,每次调用函数时都会一次又一次创建相同函数的实例(lamda语句每次都会创建新实例)。
注意:无需将事件(e)显式传递给函数。您可以在函数中使用它而无需传递它。

{<td><span onClick={(e) => this.toggle(e)}>Details</span></td>}

https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578


6

我知道这有很多答案,但是由于大多数答案都比较老(好,比较老),所以没有人提到我非常快速地成长的方法。简而言之:

使用功能组件和挂钩

在更长的时间:

尝试使用尽可能多的功能组件,而不是专门用于渲染的类,并尝试使它们尽可能纯净(是的,默认情况下,数据很脏)。

功能组件的两个明显的好处(还有更多):

  • 纯净或接近纯净使调试更加容易
  • 功能组件消除了对构造函数锅炉代码的需求

第二点的快速证明-这不是绝对令人恶心吗?

constructor(props) {
        super(props);     
        this.toggle= this.toggle.bind(this);
        this.state = {
            details: false
        } 
    }  

如果您使用功能组件做更多的事情,那么渲染就需要伟大的二重奏的第二部分-钩子。为什么它们比生命周期方法更好,它们还能做些什么,还有更多的事情会占用我很多空间,所以我建议您听一下这个人自己的话:Dan讲讲钩子

在这种情况下,您只需要两个钩子:

一个方便命名的回调挂钩useCallback。这样,您可以防止在重新渲染时反复绑定功能。

一个状态钩子,称为useState,即使整个组件都在起作用并完整执行,它也可以保持状态(是的,由于钩子的魔力,这是可能的)。在该挂钩中,将存储toggle的值。

如果您读到这一部分,您可能希望看到我在实践中谈论的所有内容并应用于原始问题。在这里,您可以: 演示

对于那些只希望浏览组件并且WTF就是这样的人,这里是:

const Item = () => {

    // HOOKZ
  const [isVisible, setIsVisible] = React.useState('hidden');

  const toggle = React.useCallback(() => {
    setIsVisible(isVisible === 'visible' ? 'hidden': 'visible');
  }, [isVisible, setIsVisible]);

    // RENDER
  return (
  <React.Fragment>
    <div style={{visibility: isVisible}}>
        PLACEHOLDER MORE INFO
    </div>
    <button onClick={toggle}>Details</button>
  </React.Fragment>
  )
};

PS:我写这篇文章是为了防止许多人因类似问题而降落在这里。希望他们至少会满意他们所看到的内容,以便对它进行更多谷歌搜索。这不是我在说其他答案是错误的,这是我在说,自从他们写的时间以来,就有另一种方法(恕我直言,更好的方法)来处理这个问题。


5

如果您不需要将参数传递给函数,只需从函数中删除(),如下所示:

<td><span onClick={this.toggle}>Details</span></td>

但是,如果要传递参数,则应执行以下操作:

<td><span onClick={(e) => this.toggle(e,arg1,arg2)}>Details</span></td>

3

ReactJS:最大更新深度超出错误

inputDigit(digit){
  this.setState({
    displayValue: String(digit)
  })

<button type="button"onClick={this.inputDigit(0)}>

为什么?

<button type="button"onClick={() => this.inputDigit(1)}>1</button>

函数onDigit设置状态,这将导致重新渲染,这将导致onDigit触发,因为这是您设置为onClick的值,这将导致设置状态而导致重新渲染,这将导致onDigit触发,因为这就是您要设置的值再…等等


3

1.如果要在调用中传递参数,则需要按如下所示调用方法。由于我们使用的是箭头函数,因此无需在中绑定方法cunstructor

onClick={() => this.save(id)} 

当我们像这样在构造函数中绑定方法时

this.save= this.save.bind(this);

那么我们需要在不传递任何参数的情况下调用方法,如下所示

onClick={this.save}

并且我们尝试在调用函数时传递参数,如下所示,那么错误就好像超出了最大深度。

 onClick={this.save(id)}

1

onClick您应该调用函数,也就是调用函数切换。

onClick={() => this.toggle()}


1

在这种情况下,此代码

{<td><span onClick={this.toggle()}>Details</span></td>}

使切换功能立即调用并一次又一次地重新呈现它,从而进行无限次调用。

因此,仅将对该切换方法的引用传递给您即可解决此问题。

所以,

{<td><span onClick={this.toggle}>Details</span></td>}

将是解决方案代码。

如果要使用(),则应使用这样的箭头函数

{<td><span onClick={()=> this.toggle()}>Details</span></td>}

如果要传递参数,则应选择最后一个选项,然后可以传递像这样的参数

{<td><span onClick={(arg)=>this.toggle(arg)}>Details</span></td>}

在最后一种情况下,它不会立即调用并且不会导致函数的重新渲染,因此避免了无限调用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.