Я пытаюсь переключить состояние компонента в ReactJS, но получаю сообщение об ошибке:

Превышена максимальная глубина обновления. Это может произойти, когда компонент многократно вызывает setState внутри componentWillUpdate или componentDidUpdate. 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 (
             
                {this.props.config.server}      
                {this.props.config.verbose} 
                {this.props.config.type}
                PLACEHOLDER MORE INFO
                {Details}
            
    )}
}

export default Item;

Maja Okholm

Ответы (11)

это из-за того, что вы вызываете toggle внутри метода рендеринга, который приведет к повторному рендерингу, и переключение вызовет снова и снова рендеринг и так далее

эта строка в вашем коде

{Details}

нужно сделать onClick ссылаться на this.toggle не вызывать его

От

до исправить проблема сделайте это

{Details}

Многие хорошие ответы, но у всех отсутствует какой-то пример с учетом хуков в 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 (
   
      setRed(red + 15)}
       onDecrease={() => setRed(red - 15)}
       color="Red"
     />
      setBlue(blue + 15)}
       onDecrease={() => setBlue(blue - 15)} 
       color="Blue" 
     />
      setGreen(green + 15)}
       onDecrease={() => setGreen(green - 15)}
       color="Green" 
     />
    
    
 )   
}

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

В данном случае это код

{Details}

вызывает немедленный вызов функции переключения и повторный рендеринг снова и снова, таким образом делая бесконечные вызовы.

, поэтому передача только ссылки на этот метод переключения решит проблему.

так,

{Details}

будет кодом решения.

Если вы хотите использовать (), вы должны использовать стрелочную функцию, подобную этой

{ this.toggle()}>Details}

Если вы хотите передать параметры, вы должны выбрать последний вариант, и вы можете передавать такие параметры, как это

{this.toggle(arg)}>Details}

В последнем случае он не вызывается немедленно и не вызывает повторного рендеринга функции, что позволяет избежать бесконечных вызовов.

Сначала забудьте о реакции:
Это не связано с реакцией и позволяет нам понять основные концепции Java Script. Например, вы написали следующую функцию в java-скрипте (имя A).

function a() {

};

Q.1) Как вызвать функцию, которую мы определили?
Ответ: a ();

Q.2) Как передать ссылку на функцию, чтобы мы могли вызвать ее последней?
Ответ: let fun = a;

Теперь, переходя к вашему вопросу, вы использовали парантез с именем функции, это означает, что функция будет вызываться при визуализации следующего оператора.

  Подробности  

Тогда как исправить?
Простой!! Просто уберите скобки. Таким образом, вы указали ссылку этой функции на событие onClick. Он обратится к вашей функции только при щелчке по вашему компоненту.

   Подробности  

Разрешено одно предложение:
Избегайте использования встроенных функций, как предлагает кто-то в ответах, это может вызвать проблемы с производительностью. Избегайте следующего кода, он будет создавать экземпляр одной и той же функции снова и снова всякий раз, когда функция будет вызвана (оператор lamda каждый раз создает новый экземпляр).
Примечание: и нет необходимости явно передавать событие (e) функции. вы можете получить к нему доступ с помощью функции, не передавая ее.

{  this.toggle (e)}> Подробности  }

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

Я знаю, что у этого есть много ответов, но поскольку большинство из них старые (ну, более старые), ни один из них не упоминает подход, который мне очень нравится очень быстро. Короче:

Используйте функциональные элементы и крючки.

В более длинном виде:

Постарайтесь использовать как можно больше функциональных компонентов вместо классовых , особенно для рендеринга, И постарайтесь сохранить их как можно более чистыми (да, данные по умолчанию грязные, я знаю).

Два откровенно очевидных преимущества функциональных компонентов (их больше):

  • Чистота или почти чистота значительно упрощают отладку
  • Функциональные компоненты устраняют необходимость в коде конструктора котла

Быстрое доказательство для 2-го пункта - Разве это не отвратительно?

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

Если вы используете функциональные компоненты не только для рендеринга, то вам понадобится вторая часть отличного дуэта - хуки. Почему они лучше методов жизненного цикла, что еще они могут сделать и многое другое займет у меня много места, поэтому я рекомендую вам послушать самого человека: Дэн проповедует крючки

В этом случае вам нужно всего два крючка:

Ловушка обратного вызова с удобным названием 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 (
  
    
PLACEHOLDER MORE INFO
) };

PS: Я написал это на случай, если сюда попадет много людей с подобной проблемой. Надеюсь, им понравится то, что я показал здесь, по крайней мере, достаточно хорошо, чтобы погуглить немного больше. Я НЕ говорю, что другие ответы неверны, это я говорю, что с тех пор, как они были написаны, есть другой способ (ИМХО, лучший) справиться с этим.

Недавно получил такую ​​ошибку:

Ошибка: ошибка Minified React # 185; посетите https://reactjs.org/docs/error-decoder.html?invariant=185 для полного сообщения или используйте неминифицированную среду разработки для полных ошибок и дополнительных полезных предупреждений.

Полный текст только что обнаруженной ошибки:

Превышена максимальная глубина обновления. Это может произойти, когда компонент многократно вызывает setState внутри componentWillUpdate или componentDidUpdate. 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 
banner count: {banners.length}
; }

service - вызов API процесса, имеет несколько методов для преобразования модели представления данных DTO. Это не имеет ничего общего с React. Может быть, у вас в проекте есть такая услуга.

Моя логика состоит в том, что баннеры модели представления строятся из товаров данных, возвращаемых из API.

useEffect ({...}, [info, goods]) имеет две зависимости: info и goods.

При изменении info и goods, useEffect хук будет повторно выполнен, установить модель просмотра баннеров, выглядит хорошо, правда?

Нет! Это вызовет утечку памяти. Хук useEffect будет выполняться бесконечно. Почему?

Поскольку при выполнении setBanner () компонент будет повторно визуализирован, оператор const info = service.makeInfo (goods); выполнится снова, вернет new info object, это приведет к изменению зависимостей useEffect, в результате чего 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]);

//... 

Кодовый ящик

если вам не нужно передавать аргументы функции, просто удалите () из функции, как показано ниже:

Details

, но если вы хотите передавать аргументы, сделайте следующее:

 this.toggle(e,arg1,arg2)}>Details

onClick вы должны вызвать функцию, которая называется переключением вашей функции.

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

ReactJS: Ошибка превышения максимальной глубины обновления

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

почему?


Функция onDigit устанавливает состояние, которое вызывает повторную визуализацию, которая вызывает срабатывание onDigit, потому что это значение, которое вы устанавливаете как onClick, который устанавливает состояние, которое вызывает повторную визуализацию, что приводит к срабатыванию onDigit, потому что это значение, которое вы ... И т. д.

1. Если мы хотим передать аргумент в вызове, нам нужно вызвать метод, как показано ниже. Поскольку мы используем стрелочные функции, нет необходимости связывать метод в конструкторе.

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

когда мы привязываем метод в конструкторе следующим образом

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

, тогда нам нужно вызвать метод без передачи аргументов, как показано ниже

onClick={this.save}

и мы пытаемся передать аргумент при вызове функции как показано ниже, ошибка возникает как превышение максимальной глубины.

 onClick={this.save(id)}

Вы должны передать объект события при вызове функции:

{ this.toggle(e)}>Details}

Если вам не нужно обрабатывать событие onClick, вы также можете ввести:

{ this.toggle()}>Details}

Теперь вы также можете добавлять свои параметры в функцию.

2022 WebDevInsider