194 votes

Composant fonctionnel sans état de React, PureComponent, Component ; quelles sont les différences et quand utiliser quoi ?

J'ai appris que de React v15.3.0 nous avons une nouvelle classe de base appelée PureComponent à prolonger avec PureRenderMixin intégré. Ce que je comprends, c'est que, sous le capot, cela emploie une comparaison superficielle des accessoires à l'intérieur de la base de données. shouldComponentUpdate .

Nous avons maintenant 3 façons de définir un composant React :

  1. Composant fonctionnel apatride qui n'étend aucune classe.
  2. Un composant qui étend PureComponent classe
  3. Un composant normal qui étend Component classe

Il y a quelque temps, nous avions l'habitude d'appeler les composants sans état "Pure Components", ou même "Dumb Components". Il semble que toute la définition du mot "pur" a maintenant changé dans React.

Bien que je comprenne les différences de base entre ces trois-là, je ne suis toujours pas sûre quand choisir quoi . Quels sont également les impacts sur les performances et les compromis de chacun ?


Mise à jour :

Ce sont les questions que j'attends de voir clarifiées :

  • Dois-je choisir de définir mes composants simples comme étant fonctionnels (par souci de simplicité) ou d'étendre PureComponent (pour des raisons de performance) ?
  • Est-ce que l'augmentation des performances que j'obtiens est un réel compromis pour la simplicité que j'ai perdue ?
  • Aurais-je jamais besoin d'étendre la normale Component alors que je peux toujours utiliser PureComponent pour de meilleures performances ?

320voto

fabio.sussetto Points 4742

Comment décider, comment choisir entre ces trois en fonction de l'objectif/de la taille/des fonctions/du comportement de nos composants ?

S'étendant de React.PureComponent ou de React.Component avec un shouldComponentUpdate ont des répercussions sur les performances. L'utilisation de composants fonctionnels apatrides est un choix "architectural" et ne présente pas (encore) d'avantages en termes de performances.

  • Pour les composants simples, uniquement de présentation, qui doivent être facilement réutilisés, préférez les composants fonctionnels sans état. Ainsi, vous êtes sûr qu'ils sont découplés de la logique de l'application, qu'ils sont faciles à tester et qu'ils n'ont pas d'effets secondaires inattendus. L'exception est si, pour une raison quelconque, vous avez beaucoup d'entre eux ou si vous avez vraiment besoin d'optimiser leur méthode de rendu (car vous ne pouvez pas définir shouldComponentUpdate pour un composant fonctionnel sans état).

  • Étendre le site PureComponent si vous savez que votre sortie dépend de props/états simples ("simple" signifie pas de structures de données imbriquées, car PureComponent effectue une comparaison superficielle) ET que vous avez besoin/possédez des améliorations de performances.

  • Étendre le site Component et mettez en œuvre votre propre shouldComponentUpdate si vous avez besoin de gains de performance en effectuant une logique de comparaison personnalisée entre les props et les états suivants/actuels. Par exemple, vous pouvez rapidement effectuer une comparaison approfondie en utilisant lodash#isEqual :

    class MyComponent extends Component {
        shouldComponentUpdate (nextProps, nextState) {
            return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
        }
    }

De même, la mise en œuvre de votre propre shouldComponentUpdate ou s'étendant de PureComponent sont des optimisations, et comme d'habitude, vous ne devriez commencer à les examiner que si vous avez des problèmes de performance ( éviter les optimisations prématurées ). En règle générale, j'essaie toujours d'effectuer ces optimisations une fois que l'application est en état de marche, avec la plupart des fonctionnalités déjà mises en œuvre. Il est beaucoup plus facile de se concentrer sur les problèmes de performances lorsqu'ils sont réellement gênants.

Plus de détails

Composants apatrides fonctionnels :

Ceux-ci sont définis en utilisant simplement une fonction. Puisqu'il n'y a pas d'état interne pour un composant sans état, la sortie (ce qui est rendu) dépend uniquement des props donnés en entrée de cette fonction.

Pour :

  • La façon la plus simple possible de définir un composant dans React. Si vous n'avez pas besoin de gérer d'état, pourquoi s'embêter avec les classes et l'héritage ? L'une des principales différences entre une fonction et une classe est qu'avec la fonction, vous êtes sûr que la sortie ne dépend que de l'entrée (pas de l'historique des exécutions précédentes).

  • Idéalement, dans votre application, vous devriez chercher à avoir le plus grand nombre possible de composants sans état, car cela signifie normalement que vous avez déplacé votre logique en dehors de la couche d'affichage et que vous l'avez déplacée vers quelque chose comme redux, ce qui signifie que vous pouvez tester votre logique réelle sans avoir à effectuer de rendu (beaucoup plus facile à tester, plus réutilisable, etc.).

Cons :

  • Aucune méthode de cycle de vie. Vous n'avez pas la possibilité de définir componentDidMount et d'autres amis. Normalement, vous le faites dans un composant parent situé plus haut dans la hiérarchie afin de transformer tous les enfants en composants apatrides.

  • Il n'y a aucun moyen de contrôler manuellement quand un nouveau rendu est nécessaire, puisque vous ne pouvez pas définir shouldComponentUpdate . Un nouveau rendu se produit à chaque fois que le composant reçoit de nouveaux props (pas de moyen de comparer superficiellement, etc.). Dans le futur, React pourrait optimiser automatiquement les composants sans état, pour l'instant il y a quelques bibliothèques que vous pouvez utiliser. Puisque les composants apatrides sont juste des fonctions, c'est le problème classique de la "mémorisation des fonctions".

  • Les références ne sont pas prises en charge : https://github.com/facebook/react/issues/4936

Un composant qui étend la classe PureComponent VS Un composant normal qui étend la classe Component :

React avait l'habitude d'avoir un PureRenderMixin vous pourriez vous attacher à une classe définie à l'aide de React.createClass syntaxe. Le mixin définirait simplement un shouldComponentUpdate effectuer une comparaison superficielle entre le props suivant et l'état suivant pour vérifier si quelque chose y a changé. Si rien n'a changé, il n'est pas nécessaire d'effectuer un nouveau rendu.

Si vous voulez utiliser la syntaxe ES6, vous ne pouvez pas utiliser les mixins. Donc, pour des raisons de commodité, React a introduit un PureComponent dont vous pouvez hériter au lieu d'utiliser Component . PureComponent ne fait que mettre en œuvre shouldComponentUpdate de la même manière que le PureRendererMixin . Il s'agit surtout d'une commodité pour que vous n'ayez pas à l'implémenter vous-même, car une comparaison superficielle entre l'état actuel/suivant et les accessoires est probablement le scénario le plus courant qui peut vous permettre de gagner rapidement en performance.

Exemple :

class UserAvatar extends Component {
    render() {
       return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
    }
} 

Comme vous pouvez le voir, la sortie dépend de props.imageUrl y props.username . Si dans un composant parent vous rendez <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" /> avec les mêmes props, React appellerait render à chaque fois, même si le résultat est exactement le même. Rappelez-vous cependant que React implémente le dom diffing, donc le DOM ne sera pas réellement mis à jour. Néanmoins, l'exécution du dom diffing peut être coûteuse, donc dans ce scénario, ce serait un gaspillage.

Si le UserAvatar Le composant s'étend PureComponent à la place, une comparaison superficielle est effectuée. Et parce que props et nextProps sont les mêmes, render ne sera pas appelé du tout.

Notes sur la définition de "pure" dans React :

En général, une "fonction pure" est une fonction dont l'évaluation donne toujours le même résultat pour la même entrée. La sortie (pour React, c'est ce qui est retourné par la fonction render ) ne dépend d'aucun historique/état et n'a pas d'effets secondaires (opérations qui modifient le "monde" en dehors de la fonction).

Dans React, les composants apatrides ne sont pas nécessairement des composants purs selon la définition ci-dessus si vous appelez "apatride" un composant qui n'appelle jamais this.setState et qui n'utilise pas this.state .

En fait, dans un PureComponent vous pouvez toujours effectuer des effets secondaires pendant les méthodes du cycle de vie. Par exemple, vous pouvez envoyer une requête ajax à l'intérieur de la méthode componentDidMount ou vous pouvez effectuer des calculs DOM pour ajuster dynamiquement la hauteur d'un div à l'intérieur du fichier render .

La définition des "composants muets" a une signification plus "pratique" (du moins selon ma compréhension) : un composant muet "se fait dire" quoi faire par un composant parent via les props, et ne sait pas comment faire les choses mais utilise les callbacks des props à la place.

Exemple d'un "smart" (intelligent) AvatarComponent :

class AvatarComponent extends Component {
    expandAvatar () {
        this.setState({ loading: true });
        sendAjaxRequest(...).then(() => {
            this.setState({ loading: false });
        });
    }        

    render () {
        <div onClick={this.expandAvatar}>
            <img src={this.props.username} />
        </div>
    }
}

Exemple d'un "muet AvatarComponent :

class AvatarComponent extends Component {
    render () {
        <div onClick={this.props.onExpandAvatar}>
            {this.props.loading && <div className="spinner" />}
            <img src={this.props.username} />
        </div>
    }
}

En fin de compte, je dirais que les notions de "muette", "apatride" et "pure" sont des concepts très différents qui peuvent parfois se chevaucher, mais pas nécessairement, en fonction principalement de votre cas d'utilisation.

1 votes

J'apprécie vraiment votre réponse et les connaissances que vous avez partagées. Mais ma vraie question est quand faut-il choisir quoi ? . Pour le même exemple que vous avez mentionné dans votre réponse, comment dois-je le définir ? Devrait-il s'agir d'un composant fonctionnel sans état (si oui, pourquoi ?), ou d'une extension de PureComponent (pourquoi ?) ou d'une extension de la classe Component (encore une fois, pourquoi ?). Comment décidez-vous, comment choisissez-vous entre ces trois éléments en fonction de la but/taille/props/comportement de nos composants ?

1 votes

Pas de problème. Pour le composant fonctionnel sans état, il y a une liste d'avantages et d'inconvénients que vous pouvez prendre en compte pour décider si cela vous convient. Cela répond-il à votre première question ? Je vais essayer de répondre un peu plus à la question du choix.

0 votes

En effet, cela a été très utile. Veuillez revoir la question, je l'ai mise à jour avec les questions qui devraient être clarifiées.

27voto

abhirathore2006 Points 2139

Je ne suis pas un génie de la réaction, mais d'après ce que je comprends, nous pouvons utiliser chaque composant dans les situations suivantes

  1. Composant apatride -- Il s'agit de composants qui n'ont pas de cycle de vie. Ces composants doivent donc être utilisés pour rendre l'élément répétitif du composant parent, par exemple pour rendre la liste de texte qui ne fait qu'afficher les informations et n'a pas d'action à effectuer.

  2. Composant pur -- Ce sont les éléments qui ont un cycle de vie et qui renvoient toujours le même résultat lorsqu'un ensemble spécifique d'accessoires est donné. Ces composants peuvent être utilisés lors de l'affichage d'une liste de résultats ou de données d'un objet spécifique qui n'a pas d'éléments enfants complexes et qui est utilisé pour effectuer des opérations qui n'ont d'impact que sur lui-même. Par exemple, l'affichage d'une liste de fiches d'utilisateurs ou d'une liste de fiches de produits (informations de base sur les produits) et la seule action que l'utilisateur peut effectuer est de cliquer pour afficher la page de détails ou ajouter au panier.

  3. Composants normaux ou composants complexes -- J'ai utilisé le terme "composant complexe" parce qu'il s'agit généralement de composants au niveau de la page et qu'ils sont constitués de nombreux composants enfants. Comme chaque enfant peut se comporter de manière unique, vous ne pouvez pas être sûr à 100% qu'il rendra le même résultat dans un état donné. Comme je l'ai dit, ces composants doivent être utilisés comme des composants conteneurs.

1 votes

Cette approche peut fonctionner, mais vous risquez de passer à côté d'importants gains de performance. Utilisation de PureComponent Les composants de niveau racine et les composants situés au sommet de votre hiérarchie sont généralement ceux qui offrent les meilleurs gains de performance. Bien sûr, vous devez éviter de muter les props et l'état directement pour que les composants purs fonctionnent correctement, mais muter les objets directement est un anti-modèle dans React de toute façon.

5voto

believesInSanta Points 179
  • React.Component est le composant "normal" par défaut. Vous les déclarez à l'aide de la balise class mot-clé et extends React.Component . Considérez-les comme une classe, avec des méthodes de cycle de vie, des gestionnaires d'événements et d'autres méthodes.

  • React.PureComponent est un React.Component qui met en œuvre shouldComponentUpdate() avec une fonction qui effectue une comparaison superficielle de son props y state . Vous devez utiliser forceUpdate() si vous savez que le composant a des props ou des données imbriquées dans l'état qui ont changé et que vous voulez refaire le rendu. Ils ne sont donc pas parfaits si vous avez besoin que les composants soient rendus à nouveau lorsque les tableaux ou les objets que vous passez comme props ou que vous définissez dans votre état changent.

  • Les composants fonctionnels sont ceux qui n'ont pas de fonctions liées au cycle de vie. Ils sont censés être sans état, mais ils sont si beaux et propres que nous avons maintenant des crochets (depuis React 16.8) pour que vous puissiez toujours avoir un état. Donc je suppose qu'ils sont juste des "composants propres".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X