ReactJS: error de profundidad de actualización máxima excedida

177

Estoy tratando de alternar el estado de un componente en ReactJS pero aparece un error que dice:

Profundidad máxima de actualización excedida. Esto puede suceder cuando un componente llama repetidamente a setState dentro de componentWillUpdate o componentDidUpdate. React limita el número de actualizaciones anidadas para evitar bucles infinitos.

No veo el bucle infinito en mi código, ¿alguien puede ayudarme?

Código de componente 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
fuente
35
Cambiar this.toggle()a this.toggleo{()=> this.toggle()}
alumno
8
Otra mejora, aunque no relacionada con su problema: ¡Conviértase toggle(){...}en toggle = () => {...}para que no lo necesite bind!
Berry M.
Gracias @learner. Tú también me ayudaste. ¿Podría explicar amablemente la razón detrás de su solución? ¿Cuál es la diferencia entre esos dos?
Shamim
2
@Shamim Es la diferencia entre llamar a una función existente y pasar la referencia a una función. Es útil comprender que estamos escribiendo código para mostrarlo y activarlo cuando el usuario hace algo, no el código que se activará tan pronto como el usuario cargue la página. reactjs.org/docs/faq-functions.html
DisplayName

Respuestas:

274

eso porque al llamar alternar dentro del método de renderizado, lo que hará que se vuelva a renderizar y alternar, se volverá a llamar y se volverá a renderizar nuevamente, etc.

esta línea en tu código

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

necesita hacer onClickreferencia a this.toggleno llamarlo

para solucionar el problema haz esto

{<td><span onClick={this.toggle}>Details</span></td>}
Ali
fuente
8
Me enfrento a una situación similar, pero necesito pasar un parámetro para alternar, ¿cómo se puede lograr esto?
Niveditha Karmegam
58
@NivedithaKarmegam Do onClick={(param) => this.toggle(param)}. Esto no se disparará inmediatamente (y volverá a aparecer). Esta es una definición de devolución de llamada (función de flecha).
Fabian Picone
15
@FabianPicone probó su método, pero cuando console.log muestra que se aprobó "param" como evento, en realidad debería hacerloonClick={() => this.toggle(param)}
iWillGetBetter
66
@iWillGetBetter Sí, el primer parámetro en onClick es el evento click. Si necesita un parámetro adicional, también puede pasarlo onClick={(event) => this.toggle(event, myParam)}.
Fabian Picone
1
Tengo esta función closeEditModal = () => this.setState({openEditModal: false});¿Cómo llamarlo en render?
Nux
31

Debe pasar el objeto de evento al llamar a la función:

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

Si no necesita manejar el evento onClick, también puede escribir:

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

Ahora también puede agregar sus parámetros dentro de la función.

Florent Philippe
fuente
2
El objeto de evento se envía automáticamente si no se especifica nada. Simplemente incluya un parámetro de entrada en la función que se llama.
Jeff Pinkston
2
{<td><span onClick={() => this.toggle(whateverParameter)}>Details</span></td>}hace el truco para mí
iWillGetBetter
22

Olvídate primero de reaccionar:
esto no está relacionado con reaccionar y déjanos entender los conceptos básicos de Java Script. Por ejemplo, ha escrito la siguiente función en Java Script (el nombre es A).

function a() {

};

Q.1) ¿Cómo llamar a la función que hemos definido?
Respuesta: a ();

Q.2) ¿Cómo pasar la referencia de función para que podamos llamarla última?
Respuesta: let fun = a;

Ahora, volviendo a su pregunta, ha utilizado la parálisis con el nombre de la función, lo que significa que se llamará a la función cuando se presente la siguiente declaración.

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

Entonces, ¿cómo corregirlo?
¡¡Sencillo!! Solo elimina el paréntesis. De esta manera, ha dado la referencia de esa función al evento onClick. Devolverá su función solo cuando haga clic en su componente.

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

Se sugirió una reacción para reaccionar:
evite usar la función en línea como lo sugirió alguien en las respuestas, puede causar problemas de rendimiento. Evite seguir el código, creará una instancia de la misma función una y otra vez cada vez que se llame a la función (la instrucción lamda crea una nueva instancia cada vez).
Nota: y no es necesario pasar el evento (e) explícitamente a la función. puedes acceder a él en la función sin pasarlo.

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

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

Piyush N
fuente
6

Sé que esto tiene muchas respuestas, pero dado que la mayoría de ellas son viejas (bueno, más viejas), ninguna menciona el enfoque por el que crezco muy rápido. En breve:

Use componentes funcionales y ganchos .

En más tiempo:

Intente utilizar tantos componentes funcionales en lugar de los de clase, especialmente para renderizar , e intente mantenerlos lo más puros posible (sí, los datos están sucios por defecto, lo sé).

Dos beneficios obvios de los componentes funcionales (hay más):

  • La pureza o casi pureza hace que la depuración sea mucho más fácil
  • Los componentes funcionales eliminan la necesidad del código de caldera del constructor

Prueba rápida para el segundo punto: ¿no es esto absolutamente desagradable?

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

Si está utilizando componentes funcionales para más que renderizar, necesitará la segunda parte de great duo - hooks. ¿Por qué son mejores que los métodos del ciclo de vida, ¿qué más pueden hacer y mucho más me llevaría mucho espacio a la cubierta por lo que te recomiendo escuchar el hombre mismo: Dan predicación de los ganchos

En este caso solo necesita dos ganchos:

Un gancho de devolución de llamada convenientemente nombrado useCallback. De esta forma, evita que la función se vincule una y otra vez cuando se vuelve a representar.

Un enlace de estado, llamado useState, para mantener el estado a pesar de que todo el componente funciona y se ejecuta en su totalidad (sí, esto es posible debido a la magia de los enlaces). Dentro de ese gancho, almacenará el valor de alternar.

Si lees esta parte, probablemente quieras ver todo lo que he hablado en acción y aplicado al problema original. Aquí tienes: Demo

Para aquellos de ustedes que solo quieren echar un vistazo al componente y de esto se trata WTF, aquí están:

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

PD: escribí esto en caso de que muchas personas aterrizaran aquí con un problema similar. Esperemos que les guste lo que ven al menos lo suficientemente bien como para googlearlo un poco más. Este no soy yo diciendo que otras respuestas están equivocadas, soy yo diciendo que desde el momento en que se escribieron, hay otra forma (en mi humilde opinión, una mejor) de tratar esto.

DanteTheSmith
fuente
5

si no necesita pasar argumentos a la función, simplemente elimine () de la función como se muestra a continuación:

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

pero si quieres pasar argumentos, debes hacer lo siguiente:

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

ReactJS: error de profundidad de actualización máxima excedida

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

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

¿por qué eso?

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

La función onDigit establece el estado, lo que provoca un rerender, lo que hace que onDigit se active porque ese es el valor que está configurando como onClick, lo que hace que se establezca el estado que provoca un rerender, lo que hace que onDigit se active porque ese es el valor que usted ' re ... Etc

Rizo
fuente
3

1.Si queremos pasar argumentos en la llamada, entonces necesitamos llamar al método como se muestra a continuación. Como estamos usando funciones de flecha, no es necesario vincular el método cunstructor.

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

cuando vinculamos el método en un constructor como este

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

entonces necesitamos llamar al método sin pasar ningún argumento como el siguiente

onClick={this.save}

e intentamos pasar argumentos mientras llamamos a la función como se muestra a continuación, entonces el error aparece como la profundidad máxima excedida.

 onClick={this.save(id)}
Aplicaciones asombrosas
fuente
1

onClick debe llamar a la función, eso se llama alternar su función.

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

makkagru
fuente
1

En este caso, este código

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

hace que la función de alternar llame inmediatamente y la vuelva a procesar una y otra vez, haciendo así llamadas infinitas.

así que pasar solo la referencia a ese método de alternar resolverá el problema.

entonces ,

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

Será el código de la solución.

Si desea usar el (), debe usar una función de flecha como esta

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

En caso de que desee pasar parámetros, debe elegir la última opción y puede pasar parámetros como este

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

En el último caso, no llama inmediatamente y no causa la reproducción de la función, evitando así llamadas infinitas.

Badri Paudel
fuente