¿Cómo forzar el montaje en componentes React?

8 minutos de lectura

avatar de usuario
o01

Digamos que tengo un componente de vista que tiene una representación condicional:

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

MyInput se parece a esto:

class MyInput extends React.Component {

    ...

    render(){
        return (
            <div>
                <input name={this.props.name} 
                    ref="input" 
                    type="text" 
                    value={this.props.value || null}
                    onBlur={this.handleBlur.bind(this)}
                    onChange={this.handleTyping.bind(this)} />
            </div>
        );
    }
}

Digamos employed es verdad. Cada vez que lo cambio a falso y la otra vista se procesa, solo unemployment-duration se reinicializa. También unemployment-reason se precarga con el valor de job-title (si se dio un valor antes de que cambiara la condición).

Si cambio el marcado en la segunda rutina de renderizado a algo como esto:

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <span>Diff me!</span>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

Parece que todo funciona bien. Parece que React simplemente no logra diferenciar ‘puesto de trabajo’ y ‘razón de desempleo’.

Por favor dime que estoy haciendo mal…

  • ¿Cuál es el data-reactid en cada uno de los MyInput (o inputcomo se ve en DOM) elementos antes y después de la employed ¿cambiar?

    – Chris

    4 de marzo de 2016 a las 9:46


  • @Chris No pude especificar que el componente MyInput representa la entrada envuelta en un <div>. los data-reactid los atributos parecen ser diferentes tanto en el div de envoltura como en el campo de entrada. job-title la entrada obtiene la identificación data-reactid=".0.1.1.0.1.0.1"tiempo unemployment-reason la entrada obtiene la identificación data-reactid=".0.1.1.0.1.2.1"

    – o01

    4 de marzo de 2016 a las 9:58


  • y que hay con unemployment-duration?

    – Chris

    4 de marzo de 2016 a las 9:59

  • @Chris Lo siento, hablé demasiado pronto. En el primer ejemplo (sin el lapso “diff me”), el reactid los atributos son idénticos en job-title y unemployment-reasonmientras que en el segundo ejemplo (con el diff span) son diferentes.

    – o01

    4 de marzo de 2016 a las 10:01


  • @Chris para unemployment-duration la reactid El atributo es siempre único.

    – o01

    4 de marzo de 2016 a las 10:04

avatar de usuario
alex k

Cambia la clave del componente.

<Component key="1" />
<Component key="2" />

El componente se desmontará y se montará una nueva instancia de Componente ya que la clave ha cambiado.

editar: Documentado en Probablemente no necesite un estado derivado:

Cuando cambia una clave, React creará una nueva instancia de componente en lugar de actualizar la actual. Las claves generalmente se usan para listas dinámicas, pero también son útiles aquí.

  • Esto debería ser mucho más obvio en la documentación de React. Esto logró exactamente lo que buscaba, pero me tomó horas encontrarlo.

    – Pablo

    22 de abril de 2018 a las 23:54

  • Bueno, esto me ayudó mucho! ¡Gracias! Necesitaba restablecer una animación CSS, pero la única forma de hacerlo es forzar una nueva representación. Estoy de acuerdo en que esto debería ser más obvio en la documentación de reacción.

    – Alex. PAGS.

    26 de julio de 2018 a las 19:43


  • @Alex.P. ¡Agradable! Las animaciones CSS pueden ser complicadas a veces. Me interesaría ver un ejemplo.

    – Alex K.

    27 de julio de 2018 a las 21:04

  • Documentación de React que describe esta técnica: reactjs.org/blog/2018/06/07/…

    – GameSalutes

    20 de enero de 2019 a las 21:38

  • Gracias. Estoy muy agradecido por esto. Intenté alimentar números aleatorios a accesorios no declarados como “randomNumber”, pero el único accesorio que funcionó fue la clave.

    – Vergüenza

    13 de junio de 2019 a las 1:47

avatar de usuario
cris

Lo que probablemente sucede es que React piensa que solo uno MyInput (unemployment-duration) se agrega entre los renders. Como tal, el job-title nunca es reemplazado por el unemployment-reasonque también es la razón por la que se intercambian los valores predefinidos.

Cuando React hace la diferencia, determinará qué componentes son nuevos y cuáles son antiguos en función de su key propiedad. Si no se proporciona dicha clave en el código, generará la suya propia.

La razón por la que funciona el último fragmento de código que proporciona es porque React esencialmente necesita cambiar la jerarquía de todos los elementos bajo el padre. div y creo que eso desencadenaría una nueva representación de todos los niños (por eso funciona). ¿Habías agregado el span hacia abajo en lugar de hacia arriba, la jerarquía de los elementos anteriores no cambiaría y esos elementos no se volverían a representar (y el problema persistiría).

Esto es lo que el oficial Reaccionar documentación dice:

La situación se vuelve más complicada cuando los elementos secundarios se mezclan (como en los resultados de búsqueda) o si se agregan nuevos componentes al principio de la lista (como en los flujos). En estos casos en los que la identidad y el estado de cada elemento secundario deben mantenerse en los pases de procesamiento, puede identificar de forma única a cada elemento secundario asignándole una clave.

Cuando React reconcilie a los niños con llave, se asegurará de que cualquier niño con llave sea reordenado (en lugar de aplastado) o destruido (en lugar de reutilizado).

Debería poder solucionar esto proporcionando un único key elemento usted mismo a cualquiera de los padres div o a todos MyInput elementos.

Por ejemplo:

render(){
    if (this.state.employed) {
        return (
            <div key="employed">
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div key="notEmployed">
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

O

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput key="title" ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput key="reason" ref="unemployment-reason" name="unemployment-reason" />
                <MyInput key="duration" ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

Ahora, cuando React haga la diferencia, verá que el divs son diferentes y lo volverán a renderizar incluyendo todos sus hijos (primer ejemplo). En el segundo ejemplo, la diferencia será un éxito en job-title y unemployment-reason ya que ahora tienen claves diferentes.

Por supuesto, puede usar las claves que desee, siempre que sean únicas.


Actualización Agosto 2017

Para una mejor comprensión de cómo funcionan las claves en React, recomiendo leer mi respuesta a Comprender las claves únicas en React.js.


Actualización noviembre 2017

Esta actualización debería haberse publicado hace un tiempo, pero al usar literales de cadena en ref ahora está en desuso. Por ejemplo ref="job-title" ahora debería ser ref={(el) => this.jobTitleRef = el} (por ejemplo). Consulte mi respuesta a la advertencia de desuso usando this.refs para obtener más información.

  • it will determine which components are new and which are old based on their key property. – eso fue… clave… para mi entendimiento

    – Larry

    17 de noviembre de 2016 a las 12:02

  • Muchas gracias ! También me di cuenta de que todos los componentes secundarios de un mapa se volvieron a renderizar con éxito y no podía entender por qué.

    – Valentin Voilean

    29 de noviembre de 2016 a las 13:07

  • ¡una buena sugerencia es usar una variable de estado (que cambia, como una identificación, por ejemplo) como la clave para el div!

    – Macilias

    8 de mayo de 2019 a las 14:06

Usar setState en su opinión para cambiar employed propiedad del estado. Este es un ejemplo del motor de renderizado React.

 someFunctionWhichChangeParamEmployed(isEmployed) {
      this.setState({
          employed: isEmployed
      });
 }

 getInitialState() {
      return {
          employed: true
      }
 },

 render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <span>Diff me!</span>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

  • Ya estoy haciendo esto. La variable ’empleada’ es parte del estado de los componentes de la vista y se cambia a través de su función setState. Lo siento por no explicar eso.

    – o01

    4 de marzo de 2016 a las 9:40

  • La variable ’empleada’ debe ser propiedad del estado React. No es obvio en su ejemplo de código.

    – Dmitriy

    4 de marzo de 2016 a las 9:41


  • ¿Cómo se cambia este.estado.empleado? Muestra el código, por favor. ¿Está seguro de que usa setState en el lugar adecuado en su código?

    – Dmitriy

    4 de marzo de 2016 a las 9:45


  • El componente de vista es un poco más complejo de lo que muestra mi ejemplo. Además de los componentes MyInput, también representa un grupo de botones de opción que controla el valor de ’empleado’ con un controlador onChange. En el controlador onChange, configuro el estado del componente de vista en consecuencia. La vista vuelve a renderizarse bien cuando cambia el valor del grupo de botones de opción, pero React cree que el primer componente MyInput es el mismo que antes de que ’empleado’ cambiara.

    – o01

    4 de marzo de 2016 a las 9:51


Estoy trabajando en Crud para mi aplicación. Así es como lo hice Obtuve Reactstrap como mi dependencia.

import React, { useState, setState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import firebase from 'firebase';
// import { LifeCrud } from '../CRUD/Crud';
import { Row, Card, Col, Button } from 'reactstrap';
import InsuranceActionInput from '../CRUD/InsuranceActionInput';

const LifeActionCreate = () => {
  let [newLifeActionLabel, setNewLifeActionLabel] = React.useState();

  const onCreate = e => {
    const db = firebase.firestore();

    db.collection('actions').add({
      label: newLifeActionLabel
    });
    alert('New Life Insurance Added');
    setNewLifeActionLabel('');
  };

  return (
    <Card style={{ padding: '15px' }}>
      <form onSubmit={onCreate}>
        <label>Name</label>
        <input
          value={newLifeActionLabel}
          onChange={e => {
            setNewLifeActionLabel(e.target.value);
          }}
          placeholder={'Name'}
        />

        <Button onClick={onCreate}>Create</Button>
      </form>
    </Card>
  );
};

Algunos React Hooks allí

¿Ha sido útil esta solución?