ReactJS: Błąd maksymalnej głębokości aktualizacji przekroczony

177

Próbuję przełączyć stan komponentu w ReactJS, ale pojawia się komunikat o błędzie:

Przekroczono maksymalną głębokość aktualizacji. Może się tak zdarzyć, gdy składnik wielokrotnie wywołuje funkcję setState wewnątrz komponentu componentWillUpdate lub componentDidUpdate. React ogranicza liczbę zagnieżdżonych aktualizacji, aby zapobiec nieskończonym pętlom.

Nie widzę nieskończonej pętli w moim kodzie, czy ktoś może pomóc?

Kod komponentu 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;
Maja Okholm
źródło
35
Zmień this.toggle()na this.togglelub{()=> this.toggle()}
uczeń
8
Kolejna poprawa, choć niezwiązana z Twoim problemem: Zmień toggle(){...}w, toggle = () => {...}aby nie było potrzeby bind!
Berry M.
Dzięki @learner. Ty też mi pomogłeś. Czy mógłbyś uprzejmie wyjaśnić powód swojego rozwiązania. Jaka jest różnica między tymi dwoma?
Shamim
2
@Shamim To różnica między wywołaniem istniejącej funkcji a przekazaniem odwołania do funkcji. Warto wiedzieć, że piszemy kod, który ma być wyświetlany i uruchamiany, gdy użytkownik coś zrobi, a nie kod, który ma być uruchamiany, gdy tylko użytkownik załaduje stronę. actjs.org/docs/faq-functions.html
DisplayName

Odpowiedzi:

274

że ponieważ wywołujesz toggle wewnątrz metody renderowania, co spowoduje ponowne renderowanie i przełączenie, wywoła ponownie i ponownie renderuje i tak dalej

ten wiersz w twoim kodzie

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

musisz onClickodwołać się do this.toggletego, aby go nie nazywać

aby rozwiązać problem, zrób to

{<td><span onClick={this.toggle}>Details</span></td>}
Ali
źródło
8
Mam podobną sytuację, ale muszę przekazać parametr, aby przełączyć, jak można to osiągnąć?
Niveditha Karmegam
58
@NivedithaKarmegam Do onClick={(param) => this.toggle(param)}. To nie zostanie uruchomione natychmiast (i ponownie się wyłączy). To jest definicja wywołania zwrotnego (funkcja strzałki).
Fabian Picone
15
@FabianPicone wypróbował twoją metodę, ale kiedy I console.log, pokazuje, że "param" zostało przekazane jako zdarzenie, powinno faktycznie zrobićonClick={() => this.toggle(param)}
iWillGetBetter
6
@iWillGetBetter Tak, pierwszym parametrem w onClick jest zdarzenie kliknięcia. Jeśli potrzebujesz dodatkowego parametru, możesz go również przekazać onClick={(event) => this.toggle(event, myParam)}.
Fabian Picone,
1
Mam taką funkcję. closeEditModal = () => this.setState({openEditModal: false});Jak to wywołać w renderowaniu?
Nux
31

Przy wywołaniu funkcji należy przekazać obiekt zdarzenia:

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

Jeśli nie potrzebujesz obsługi zdarzenia onClick, możesz również wpisać:

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

Teraz możesz również dodać swoje parametry w funkcji.

Florent Philippe
źródło
2
Obiekt zdarzenia jest wysyłany automatycznie, jeśli nic nie zostanie określone. Po prostu dołącz parametr wejściowy do wywoływanej funkcji.
Jeff Pinkston
2
{<td><span onClick={() => this.toggle(whateverParameter)}>Details</span></td>}
załatwia
22

Zapomnij najpierw o reakcji:
nie jest to związane z reagowaniem i pozwól nam zrozumieć podstawowe pojęcia Java Script. Na przykład napisałeś następującą funkcję w skrypcie java (nazwa to A).

function a() {

};

P.1) Jak wywołać zdefiniowaną przez nas funkcję?
Ans: a ();

P.2) Jak przekazać odniesienie do funkcji, abyśmy mogli ją nazwać drugą?
Odp: niech zabawa = a;

Przechodząc do twojego pytania, użyłeś parantezy z nazwą funkcji, co oznacza, że ​​funkcja zostanie wywołana, gdy zostanie wyświetlona następująca instrukcja.

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

Więc jak to poprawić?
Prosty!! Po prostu usuń nawiasy. W ten sposób dałeś odniesienie tej funkcji do zdarzenia onClick. Wywoła funkcję tylko wtedy, gdy zostanie kliknięty komponent.

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

Jedna sugestia wydana, aby zareagować:
Unikaj używania funkcji inline zgodnie z sugestią kogoś w odpowiedzi, może to spowodować problemy z wydajnością. Unikaj podążania za kodem, będzie tworzył instancję tej samej funkcji za każdym razem, gdy funkcja zostanie wywołana (instrukcja lamda tworzy nową instancję za każdym razem).
Uwaga: i nie ma potrzeby jawnego przekazywania zdarzenia (e) do funkcji. możesz uzyskać do niego dostęp w funkcji bez jej przekazywania.

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

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

Piyush N
źródło
6

Wiem, że ma wiele odpowiedzi, ale ponieważ większość z nich jest stara (no cóż, starsza), żadna nie wspomina o podejściu, na które bardzo szybko się rozwijam. W skrócie:

Użyj funkcjonalnych elementów i haczyków .

Dłużej:

Spróbuj użyć jak największej liczby komponentów funkcjonalnych zamiast klasowych, szczególnie do renderowania , ORAZ staraj się, aby były tak czyste, jak to tylko możliwe (tak, dane są domyślnie brudne, wiem).

Dwie wprost oczywiste zalety komponentów funkcjonalnych (jest ich więcej):

  • Czystość lub prawie czystość znacznie ułatwia debugowanie
  • Komponenty funkcjonalne eliminują potrzebę stosowania kodu kotła konstruktora

Szybki dowód na drugi punkt - czy to nie jest absolutnie obrzydliwe?

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

Jeśli używasz funkcjonalnych komponentów do więcej niż renderowania, będziesz potrzebować drugiej części świetnego duetu - haczyków. Dlaczego są lepsze niż metody cyklu życia, co jeszcze mogą zrobić i wiele więcej zajęłoby mi dużo miejsca, więc polecam posłuchać samego mężczyzny: Dan głosi haczyki

W takim przypadku potrzebujesz tylko dwóch haczyków:

Hak callback o dogodnej nazwie useCallback. W ten sposób zapobiegasz ciągłemu wiązaniu funkcji podczas ponownego renderowania.

Hak stanu, wywoływany useStatedo utrzymywania stanu, mimo że cały komponent jest funkcjonalny i wykonywany w całości (tak, jest to możliwe dzięki magii zaczepów). Wewnątrz tego haka będziesz przechowywać wartość toggle.

Jeśli czytasz tę część, prawdopodobnie chcesz zobaczyć wszystko, o czym mówiłem w akcji i zastosowałem do pierwotnego problemu. Proszę bardzo: Demo

Dla tych z Was, którzy chcą tylko rzucić okiem na komponent, a WTF jest o tym, oto jest:

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: Napisałem to na wypadek lądowania tu wielu ludzi z podobnym problemem. Miejmy nadzieję, że spodoba im się to, co widzą, przynajmniej na tyle dobrze, że będą go bardziej wygooglować. To nie ja mówię, że inne odpowiedzi są błędne, to ja mówię, że odkąd zostały napisane, istnieje inny sposób (IMHO, lepszy) radzenia sobie z tym.

DanteTheSmith
źródło
5

jeśli nie musisz przekazywać argumentów do funkcji, po prostu usuń () z funkcji, jak poniżej:

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

ale jeśli chcesz przekazywać argumenty, powinieneś zrobić jak poniżej:

<td><span onClick={(e) => this.toggle(e,arg1,arg2)}>Details</span></td>
Abolfazl Miadian
źródło
3

ReactJS: Błąd maksymalnej głębokości aktualizacji przekroczony

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

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

Dlaczego to?

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

Funkcja onDigit ustawia stan, który powoduje ponowne wysłanie, co powoduje uruchomienie onDigit, ponieważ jest to wartość, którą ustawiasz jako onClick, która powoduje ustawienie stanu, który powoduje ponowne wysłanie, co powoduje uruchomienie onDigit, ponieważ jest to wartość, którą ' ponownie… Itd

Rizo
źródło
3

Jeśli chcemy przekazać argument w wywołaniu, musimy wywołać metodę jak poniżej Ponieważ używamy funkcji strzałkowych, nie ma potrzeby wiązania metody w cunstructor.

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

kiedy łączymy metodę w konstruktorze w ten sposób

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

następnie musimy wywołać metodę bez przekazywania żadnego argumentu, jak poniżej

onClick={this.save}

i próbujemy przekazać argument podczas wywoływania funkcji, jak pokazano poniżej, wtedy błąd pojawia się jak przekroczona maksymalna głębokość.

 onClick={this.save(id)}
Niesamowite aplikacje
źródło
1

onClick powinieneś wywołać funkcję, która nazywa się przełącznikiem funkcji.

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

makkagru
źródło
1

W tym przypadku ten kod

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

powoduje natychmiastowe wywołanie funkcji toggle i ponowne jej renderowanie, w ten sposób wykonując nieskończone wywołania.

więc przekazanie tylko odniesienia do tej metody przełączania rozwiąże problem.

więc ,

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

będzie kod rozwiązania.

Jeśli chcesz użyć (), powinieneś użyć funkcji strzałki, takiej jak ta

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

Jeśli chcesz przekazać parametry, powinieneś wybrać ostatnią opcję i możesz przekazać takie parametry

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

W ostatnim przypadku nie wywołuje natychmiast i nie powoduje ponownego renderowania funkcji, stąd unika się nieskończonych wywołań.

Badri Paudel
źródło