web-dev-qa-db-fra.com

ReactJS - Est-ce que le rendu s'appelle à tout moment "setState" est appelé?

Est-ce que React rend à nouveau tous les composants et sous-composants chaque fois que setState est appelé?

Si oui, pourquoi? Je pensais que l'idée était que React ne rendait que le minimum nécessaire - lorsque l'état changeait. 

Dans l'exemple simple suivant, les deux classes sont à nouveau rendues lorsque le texte est cliqué, malgré le fait que l'état ne change pas lors des clics suivants, car le gestionnaire onClick définit toujours la valeur state à la même valeur:

this.setState({'test':'me'});

Je me serais attendu à ce que les rendus ne se produisent que si les données state ont changé.

Voici le code de l'exemple, en tant que JS Fiddle , et l'extrait de code incorporé:

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>

[1]: http://jsfiddle.net/fp2tncmb/2/
361
Brad Parks

Est-ce que React rend à nouveau tous les composants et sous-composants chaque fois que setState est appelé?

Par défaut - oui.

Il existe une méthode boolean shouldComponentUpdate (objet nextProps, objet nextState), chaque composant a cette méthode et il est de sa responsabilité de déterminer "le composant doit-il mettre à jour (exécuter render fonction)?" chaque fois que vous changez état ou passez un nouveau props du composant parent.

Vous pouvez écrire votre propre implémentation de la méthode shouldComponentUpdate pour votre composant, mais l'implémentation par défaut retourne toujours true - ce qui signifie toujours réexécuter la fonction de rendu.

Citation tirée de la documentation officielle http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

ShouldComponentUpdate retourne toujours true par défaut pour empêcher bugs subtils lorsque l'état est muté en place, mais si vous faites attention à toujours traiter l’état comme immuable et à ne lire que des accessoires et l’état Dans render (), vous pouvez remplacer shouldComponentUpdate par un mise en œuvre qui compare les anciens accessoires et l'état à leur remplaçants.

Partie suivante de votre question:

Si oui, pourquoi? Je pensais que l'idée était que React ne rendait que le minimum nécessaire - lorsque l'état changeait. 

Il y a deux étapes dans ce que nous pourrions appeler "rendre":

  1. Rendu du DOM virtuel: lorsque la méthode render est appelée, elle retourne une nouvelle structure dom virtuel du composant. Comme je l'ai déjà mentionné, cette méthode render est toujours appelée lorsque vous appelez setState (), car shouldComponentUpdate _ renvoie toujours true par défaut. Donc, par défaut, il n’ya pas d’optimisation ici dans React.

  2. Rendu natif du DOM: React modifie les nœuds DOM réels de votre navigateur uniquement s'ils ont été modifiés dans le DOM virtuel et aussi peu que nécessaire - il s'agit de la fonctionnalité fantastique de React qui optimise la mutation DOM réelle et rend React rapide.

431
Petr

Non, React ne rend pas tout le rendu lorsque l'état change.

  • Lorsqu'un composant est sale (son état a changé), ce composant et ses enfants sont restitués. Ceci, dans une certaine mesure, consiste à refaire le moins possible. Le seul moment où le rendu n'est pas appelé est le moment où une branche est déplacée vers une autre racine, où théoriquement nous n'avons pas besoin de re-rendre quoi que ce soit. Dans votre exemple, TimeInChild est un composant enfant de Main. Il est donc également restitué lorsque l'état de Main est modifié.

  • React ne compare pas les données d'état. Lorsque setState est appelé, il marque le composant comme étant sale (ce qui signifie qu'il doit être restitué). La chose importante à noter est que, bien que la méthode render du composant soit appelée, le DOM réel n'est mis à jour que si la sortie est différente de l'arborescence DOM actuelle (la différence a.k.a entre l'arborescence DOM virtuelle et celle du document). Dans votre exemple, même si les données state n'ont pas changé, l'heure de la dernière modification l'a été, ce qui a rendu Virtual DOM différent du DOM du document, d'où la mise à jour du code HTML.

84
tungd

Oui. Il appelle la méthode render () chaque fois que nous appelons setState lorsque "shouldComponentUpdate" renvoie false. 

3
lakmalsathyajith

Même si cela est indiqué dans de nombreuses autres réponses, le composant doit soit:

  • implémenter shouldComponentUpdate pour effectuer le rendu uniquement lorsque l'état ou les propriétés changent

  • passez à l'extension d'un PureComponent , qui implémente déjà une méthode shouldComponentUpdate en interne pour des comparaisons superficielles.

Voici un exemple qui utilise shouldComponentUpdate, qui ne fonctionne que pour ce cas d'utilisation simple et à des fins de démonstration. Lorsque cela est utilisé, le composant ne se restitue plus à chaque clic et est rendu lors de son premier affichage, et après avoir été cliqué une fois.

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    shouldComponentUpdate: function(nextProps, nextState) {
      if (this.state == null)
        return true;
  
      if (this.state.test == nextState.test)
        return false;
        
      return true;
  },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>

0
Brad Parks