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


179

Я пытаюсь переключить состояние компонента в 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 (
            <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!
Берри М.

Спасибо @learner. Вы помогли мне тоже. Не могли бы вы объяснить причину вашего решения. В чем разница между этими двумя?
Шамим

2
@Shamim Это разница между вызовом существующей функции и передачей ссылки на функцию. Полезно понимать, что мы пишем код, который будет отображаться и запускаться, когда пользователь что-то делает, а не код, который должен запускаться, как только пользователь загружает страницу. reactjs.org/docs/faq-functions.html
DisplayName

Ответы:


275

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

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

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

Вы должны сделать onClickссылку, чтобы this.toggleне звонить

чтобы решить проблему, сделайте это

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

9
Я сталкиваюсь с подобной ситуацией, но мне нужно передать параметр для переключения, как это можно сделать?
Ниведита Кармегам

58
@NivedithaKarmegam Do onClick={(param) => this.toggle(param)}. Это не сработает мгновенно (и перезапустится). Это определение обратного вызова (функция стрелки).
Фабиан Пиконе

16
@FabianPicone попробовал ваш метод, но когда я console.log показывает, что «param» был передан как событие, он должен это сделатьonClick={() => this.toggle(param)}
iWillGetBetter

6
@iWillGetBetter Да, первым параметром в onClick является событие click. Если вам нужен дополнительный параметр, вы можете также передать его onClick={(event) => this.toggle(event, myParam)}.
Фабиан Пикон

1
У меня есть эта функция closeEditModal = () => this.setState({openEditModal: false});Как вызвать ее в рендер?
Nux

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>}делает трюк для меня
iWillGetBetter

22

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

function a() {

};

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

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

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

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

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

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

Хук обратного вызова с удобным названием useCallback. Таким образом, вы предотвращаете привязку функции снова и снова при повторном рендеринге.

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

Если вы прочитаете эту часть, вы, вероятно, захотите увидеть все, о чем я говорил, в действии и применить к исходной проблеме. Здесь вы идете: Демо

Для тех из вас, кто хочет только взглянуть на компонент и 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>}

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.