2
votes

Quelles sont les garanties sur l'ordre des événements du cycle de vie React entre les composants?

Quelles sont les garanties sur l'ordre du cycle de vie de React sur des composants séparés, et sont-elles clairement documentées n'importe où?

Par exemple, étant donné que j'ai:

<div>{ x ? <A /> : <B /> }</div>

Ici, x changer entre true et false démontera un composant et montera l'autre.

Y a-t-il des garanties sur l'ordre dans lequel les événements du cycle de vie impliqués dans le montage seront se déclencher sur ces éléments?

Par exemple, il est bien documenté que le rendu pour A se déclenchera avant componentDidMount pour A lorsque x deviendra vrai. Cependant, est-il garanti que render et componentDidMount pour A se déclencheront toujours après componentWillUnmount pour B?

Pour prendre ceci plus loin: cela change-t-il si A et B sont des enfants plus bas dans cet arbre, avec le commutateur en haut?

Toutes les réponses sont les bienvenues, mais une documentation solide à ce sujet est très appréciée.

p>


2 commentaires

Bien qu'il n'y ait aucune garantie répertoriée n'importe où dans la documentation, sur la base de mon essai, il semble que lors de la mise à jour de l'état, c'est-à-dire que x se met à jour sur false, B est appelé et son constructeur < / code> et la méthode render s'exécutent après quoi le démontage de A se produit. Les deux sont des composants isolés et vous ne savez pas quelle est votre crainte de voir le cycle de vie individuel s'exécuter à un moment donné. Dites-le comme même le démontage ne se produit pas avant le montage d'un autre composant, pourquoi cela importerait-il car la réconciliation de React ne se souciera pas du composant en cours de cycle de démontage


Dans mon cas, pour des raisons indépendantes, je modifie directement le DOM lors du montage / démontage, et A & B ne sont pas isolés l'un de l'autre - ils éditent tous les deux le même DOM. Si willMount for B s'exécute avant didUnmount de A, mon code peut échouer. Je vais essayer de rendre cela plus résilient, mais ce serait plus facile de le faire avec une idée sur les commandes possibles ici.


3 Réponses :


0
votes

Parfois, j'avais du mal à comprendre les différences fondamentales entre les méthodes componentWillMount () et componentDidMount (). J'étais frustré quand on travaillait, mais je ne comprenais ni comment ni pourquoi.

composantWillMount () Un gros hic que je n'ai pas réalisé avant une session de groupe d'étude en ligne est que componentWillMount () a été obsolète en 2018. Bien que vous puissiez continuer à l'utiliser (jusqu'à React 17.0), ce n'est pas une bonne pratique car il n'est pas sûr pour rendu asynchrone. Si vous choisissez de continuer à l'utiliser, vous devez utiliser UNSAFE_componentWillMount ().

La raison L'utilisation d'un appel de récupération dans componentWillMount () entraîne le rendu du composant avec des données vides au début, car componentWillMount () ne retournera PAS avant le premier rendu du composant.

En raison du fait que les événements JavaScript sont asynchrones, lorsque vous effectuez un appel API, le navigateur continue d'effectuer d'autres tâches pendant que l'appel est toujours en mouvement. Avec React, pendant le rendu d'un composant, il n'attend pas la fin de componentWillMount (), de sorte que le composant continue à être rendu.

Cela dit, vous devrez créer un composant qui semble toujours présentable sans les données que vous espérez afficher. Il n'y a aucun moyen (pas même une minuterie) d'arrêter le rendu du composant tant que les données ne sont pas présentes.

Lorsque je construisais mon projet, je n’étais pas au courant de la dépréciation de componentWillMount () et je ne comprenais pas pourquoi je continuais à recevoir l’erreur «impossible de lire la propriété‘ map ’of undefined». J'étais tellement confus parce que j'avais un tableau et qu'il devrait être rempli de données, mais ce n'était pas le cas.

Il s'avère que cela a un sens complet et total puisque le composant est d'abord rendu avec des données vides, donc le tableau était vide et ne pouvait pas être itéré. Ce n'est qu'à ce stade que j'ai appris qu'il était préférable d'utiliser componentDidMount ().

componentDidMount () Le meilleur endroit pour effectuer des appels pour récupérer des données est dans componentDidMount (). componentDidMount () n'est appelé qu'une seule fois, sur le client, par rapport à componentWillMount () qui est appelé deux fois, une fois sur le serveur et une fois sur le client. Il est appelé après le rendu initial lorsque le client a reçu des données du serveur et avant que les données ne soient affichées dans le navigateur. Étant donné que les données ne seront chargées qu'après le rendu initial, le développeur DOIT configurer correctement l'état initial des composants.

Par ailleurs, avec componentDidMount (), vous pouvez utiliser un minuteur et mettre à jour les données de temps en temps sans que l'utilisateur n'ait à actualiser la page. Je dirais que c'est une fonctionnalité vraiment intéressante qui peut être utile dans les projets / sites Web.


0 commentaires

1
votes

componentDidUnmount n'est jamais appelé en rendu conditionnel.

J'ai testé sur codepen et obtenez le résultat comme ci-dessous:

Composant parent

"B will be mounted"  // first render
"B is mounted"

"B will be unmounted" // first click
"A will be mounted"
"A is mounted"

"A will be unmounted" // second click
"B will be mounted"
"B is mounted"

Composant enfant A

Almost the same as A :D

Composant enfant B

class A extends React.Component {
  componentDidMount() {
    console.log('A is mounted');
  }
  componentWillMount() {
    console.log('A will be mounted');
  }
  componentWillUnmount() {
    console.log('A will be unmounted');
  }
  componentDidUnmount() {
    console.log('A is unmounted');
  }
  render() {
    return (
      <h1>A</h1>
    );
  }
}

Résultat

J'ai cliqué deux fois sur CONVERT et le résultat était:

  constructor(props) {
    super(props);
    this.state = {
      flag: false,
    }
  }
  render() {
    const { flag } = this.state;
    return (
      <div>
        <h2 onClick={() => this.setState({flag: !flag})}>CONVERT</h2>
        <div>
          { flag ? <A /> : <B /> }
        </div>
      </div>
    );
  }

componentDidUnmount n'a jamais été appelé dans le rendu conditionnel.


1 commentaires

J'ai fait une erreur dans la question - je voulais dire: est-ce que didMount pour A se produira toujours après willUnmount pour B, et non didUnmount. La commande ici est toujours utile, merci! Connaissez-vous des sources ou de la documentation à ce sujet? Je dois vraiment trouver ici les garanties globales et les invariants, plutôt que des exemples spécifiques.



7
votes

Si vous regardez les tests dans le référentiel officiel de React, vous pouvez facilement trouver les tests pertinents liés à cette commande.


When replacing <A /> with <B />, B.componentWillMount now always
happens before A.componentWillUnmount. Previously,
A.componentWillUnmount could fire first in some cases.

Le raisonnement derrière cette implémentation peut être trouvé @ commit message pour le test

"Cela correspond à ce que nous faisons dans Fiber - et le faire de cette façon est le seul comment nous pouvons préparer de nouvelles vues en arrière-plan avant de démonter les anciennes ceux. "


Consultez également ce journal des modifications récent pour React 16.0.0: React 16.0.0 changelog

 it('prepares new child before unmounting old', () => {
    const log = [];

    class Spy extends React.Component {
      UNSAFE_componentWillMount() {
        log.push(this.props.name + ' componentWillMount');
      }
      render() {
        log.push(this.props.name + ' render');
        return <div />;
      }
      componentDidMount() {
        log.push(this.props.name + ' componentDidMount');
      }
      componentWillUnmount() {
        log.push(this.props.name + ' componentWillUnmount');
      }
    }

    class Wrapper extends React.Component {
      render() {
        return <Spy key={this.props.name} name={this.props.name} />;
      }
    }

    const container = document.createElement('div');
    ReactDOM.render(<Wrapper name="A" />, container);
    ReactDOM.render(<Wrapper name="B" />, container);

    expect(log).toEqual([
      'A componentWillMount',
      'A render',
      'A componentDidMount',

      'B componentWillMount',
      'B render',
      'A componentWillUnmount',
      'B componentDidMount',
    ]);
  });

Donc cet ordre est garanti!


1 commentaires

Excellente réponse, c'est exactement ce que je cherchais, merci!