我试图在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;

这是因为你在渲染方法中调用toggle,这将导致重新渲染,toggle将再次调用,再次重新渲染,等等。

代码中的这一行:

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

你需要使onClick指向这个。切换而不是调用它。

要解决这个问题,请这样做:

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

你应该在调用函数时传递事件对象:

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

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

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

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


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

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

但是如果你想传递参数,你应该像下面这样做:

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

先忘掉react: 这与react无关,让我们了解Java Script的基本概念。例如,您已经在java脚本中编写了以下函数(名称为A)。

function a() {

};

问题1)如何调用我们已经定义的函数? 答:一个();

问题2)如何传递函数的引用,以便稍后调用它? 答:让乐趣= a;

现在回到你的问题,你已经使用了函数名的paranthesis,这意味着当下面的语句将被渲染时,函数将被调用。

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

那么如何改正呢? 简单! !只要去掉括号。通过这种方式,您已经将该函数的引用提供给onClick事件。它只在单击组件时才会回调函数。

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

一个与反应相关的建议: 避免使用有人在回答中建议的内联函数,这可能会导致性能问题。 避免下面的代码,它将在函数被调用时一次又一次地创建同一个函数的实例(lamda语句每次都会创建新实例)。 注意:并且不需要显式地将事件(e)传递给函数。你可以在函数中访问它而不传递它。

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

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


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触发,因为那是你的值。等等


onClick你应该调用函数,这叫做你的函数切换。

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


我知道这个问题有很多答案,但因为他们大多数都是老的(好吧,更老了),没有人提到我非常喜欢的方法。简而言之:

使用功能组件和钩子。

长:

尝试使用尽可能多的函数组件,而不是类组件,特别是用于渲染,并尽量保持它们尽可能纯粹(是的,数据在默认情况下是脏的)。

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

纯粹性或接近纯粹性使调试变得更加容易 功能组件不需要构造函数锅炉代码

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

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

如果你使用功能组件进行更多的渲染,你将需要伟大的duo - hook的第二部分。为什么它们比生命周期方法更好,它们还能做什么,以及更多的东西,这将占用我很多空间,所以我建议你听他自己说: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:我写这个是为了防止很多人有类似的问题。希望他们会喜欢我在这里展示的东西,至少好到可以再谷歌一点。这不是我说其他答案是错误的,这是我说,自从它们被写出来以来,有另一种方法(恕我直言,更好的方法)来处理这个问题。


1.如果我们想在调用中传递参数,那么我们需要像下面这样调用方法 因为我们使用的是箭头函数,所以不需要在构造函数中绑定方法。

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

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

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

然后,我们需要调用该方法而不传递任何参数,如下所示

onClick={this.save}

我们尝试在调用函数时传递参数 如下所示,然后误差就像最大深度超出。

 onClick={this.save(id)}

在本例中,就是这段代码

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

导致切换函数立即调用,并重新呈现它一次又一次,从而进行无限的调用。

因此,只传递对toggle方法的引用就可以解决问题。

所以,

{<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>}

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


很多好答案,但都遗漏了一些例子,考虑到react/ react native中的钩子

正如上面的回答中所写的,回调不应该在子组件内部被“调用”,而只能被引用。

这意味着什么? 让我们考虑一个父组件,它有2个改变rgb颜色的子组件:

import React, { useState } from "react"
import {View, Text, StyleSheet } from "react-native"
import ColorCounter from "../components/ColorCounter"

const SquareScreen = () =>{
  const [red, setRed] = useState(0)
  const [blue, setBlue] = useState(0)
  const [green, setGreen] = useState(0)

 return (
   <View>
     <ColorCounter 
       onIncrease={() => setRed(red + 15)}
       onDecrease={() => setRed(red - 15)}
       color="Red"
     />
     <ColorCounter 
       onIncrease={() => setBlue(blue + 15)}
       onDecrease={() => setBlue(blue - 15)} 
       color="Blue" 
     />
     <ColorCounter 
       onIncrease={() => setGreen(green + 15)}
       onDecrease={() => setGreen(green - 15)}
       color="Green" 
     />
    <View 
      style={{ 
        height:150,
        width:150, 
        backgroundColor:`rgb(${red},${blue},${green})`
      }}
    />
    </View>
 )   
}

const styles = StyleSheet.create({})

export default SquareScreen

这是子按钮组件:

import React, { useState } from "react"
import {View, Text, StyleSheet, Button } from "react-native"

const ColorCounter = ({color, onIncrease, onDecrease}) =>{
  return (
    <View>
      <Text>{color}</Text>
      <Button onPress={onIncrease}  title={`Increase ${color}`} /> --> here if you use onPress={onIncrease()} this would cause a call of setColor(either setRed,SetBlue or setGreen) that call again onIncrease and so on in a loop)
      <Button onPress={onDecrease}  title={`Decrease ${color}`} />
    </View>  
  )  
}

export default ColorCounter

最近我得到了这个错误:

错误:迷你反应错误#185;访问https://reactjs.org/docs/error-decoder.html?invariant=185获取完整消息,或者使用非简化的开发环境获取完整错误和其他有用警告。

您刚刚遇到的错误的全文如下:

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

好的。这是我的案例,我使用react函数组件+ react钩子。让我们先看看错误的示例代码:

import { useEffect, useState } from "react";
const service = {
  makeInfo(goods) {
    if (!goods) return { channel: "" };
    return { channel: goods.channel };
  },
  getGoods() {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve({
          channel: "so",
          id: 1,
          banners: [{ payway: "visa" }, { payway: "applepay" }]
        });
      }, 1000);
    });
  },
  makeBanners(info, goods) {
    if (!goods) return [];
    return goods.banners.map((v) => {
      return { ...v, payway: v.payway.toUpperCase() };
    });
  }
};
export default function App() {
  const [goods, setGoods] = useState();
  const [banners, setBanners] = useState([]);

  useEffect(() => {
    service.getGoods().then((res) => {
      setGoods(res);
    });
  }, []);

  const info = service.makeInfo(goods);

  useEffect(() => {
    console.log("[useEffect] goods: ", goods);
    if (!goods) return;
    setBanners(service.makeBanners({}, goods));
  }, [info, goods]);

  return <div>banner count: {banners.length}</div>;
}

服务过程API调用,并有一些方法转换DTO数据视图模型。这与React无关。也许你的项目中有这样的服务。

我的逻辑是,横幅视图模型是从API返回的商品数据构建的。

useEffect({…}, [info, goods])有两个依赖项:info和goods。

当info和goods发生变化时,useEffect钩子将重新执行,设置横幅视图模型,看起来不错,对吧?

不!这将导致内存泄漏。useEffect钩子将无限地执行。为什么?

因为当setBanner()执行时,组件将重新呈现,const info = service.makeInfo(goods);语句将再次执行,返回一个新的info对象,这将导致useEffect的deps改变,导致useEffect再次执行,形成一个死循环。

解决方案:使用useMemo返回一个记忆值。使用这个记忆值作为useEffect钩子的依赖项。

// ...
 const info = useMemo(() => {
    return service.makeInfo(goods);
  }, [goods]);

  useEffect(() => {
    console.log("[useEffect] goods: ", goods);
    if (!goods) return;
    setBanners(service.makeBanners({}, goods));
  }, [info, goods]);

//... 

Codesandbox


当我使用useEffect像这个useEffect(() =>{}),添加[]到它,像这个useEffect(() =>{},[])。


在我的例子中,这是由于无限循环。

我的一个函数是设置状态,组件正在重新加载,该组件依次导致函数重新运行,然后函数再次设置状态。这个循环一直到无穷。

为了避免这种情况,我把我的函数包装在useEffect钩子中,并给它一些依赖,这样这个函数只在依赖被改变时运行。


只需删除(),但如果是useEffect的情况,则

const [isInitialRender, setIsInitialRender] = useState(true); useEffect(() => { const data = localStorage.getItem("auth"); 如果(isInitialRender) { setIsInitialRender(假); If(数据){ const parsed = JSON.parse(data); setAuth({…认证,用户:已解析。用户,令牌:已解析。令牌}); } } }, [auth, isInitialRender]);

isInitialRender true和false将避免你陷入循环