web-dev-qa-db-fra.com

React composant fonctionnel sans état, PureComponent, Component; Quelles sont les différences et quand devrions-nous utiliser quoi?

Sachez que depuis React v15.3.0 , nous avons une nouvelle classe de base appelée PureComponent à étendre avec PureRenderMixin built-in. Ce que je comprends, c’est que, sous le capot, ceci utilise une comparaison peu profonde des accessoires à l’intérieur de shouldComponentUpdate.

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

  1. Composant fonctionnel sans état qui ne s'étend à aucune classe
  2. Un composant qui étend la classe PureComponent
  3. Un composant normal qui étend la classe Component

Il y a quelque temps, nous appelions des composants sans état des composants purs, voire des composants stupides. On dirait que toute la définition du mot "pur" a maintenant changé dans React.

Bien que je comprenne les différences fondamentales entre ces trois, je ne suis toujours pas sûr quand choisir quoi . Aussi, quels sont les impacts sur la performance et les compromis de chacun?


Mettre à jour :

Voici la question que je compte clarifier:

  • Devrais-je choisir de définir mes composants simples comme fonctionnels (pour des raisons de simplicité) ou d’étendre la classe PureComponent (pour des raisons de performances)?
  • L’augmentation de la performance que j’obtiens est un réel compromis pour la simplicité simplifiée que j’ai perdue?
  • Aurais-je jamais besoin d'étendre la classe Component normale quand je peux toujours utiliser PureComponent pour de meilleures performances?
166
free-soul

Comment décidez-vous, comment choisissez-vous entre ces trois critères en fonction de l'objectif/de la taille/des accessoires/du comportement de nos composants?

L'extension de React.PureComponent ou de React.Component avec une méthode shouldComponentUpdate personnalisée a des conséquences sur les performances. L'utilisation de composants fonctionnels sans état est un choix «architectural» et ne présente pas (pour le moment) d'avantages en termes de performances.

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

  • Étendez PureComponent si vous savez que votre sortie dépend de propriétés/état simples ("simple", ce qui signifie qu'aucune structure de données imbriquée, PureComponent effectuant une comparaison superficielle) ET que vous avez besoin/pouvez améliorer certaines performances.

  • Étendez Component et implémentez votre propre shouldComponentUpdate si vous avez besoin de gains de performances en effectuant une logique de comparaison personnalisée entre les accessoires et l'état 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 plus, l'implémentation de votre propre shouldComponentUpdate ou l'extension de PureComponent sont des optimisations, et comme d'habitude, vous devriez commencer à vérifier cela uniquement si vous avez des problèmes de performances ( évitez les optimisations prématurées ). En règle générale, j'essaie toujours de faire ces optimisations lorsque l'application est en état de fonctionnement, la plupart des fonctionnalités étant déjà implémentées. Il est beaucoup plus facile de se concentrer sur les problèmes de performance quand ils se mettent en travers.

Plus de détails

Composants fonctionnels sans état:

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

Avantages: 

  • Méthode la plus simple possible pour définir un composant dans React. Si vous n'avez besoin de gérer aucun état, pourquoi se préoccuper des classes et de 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 (et non de l'historique des exécutions précédentes).

  • Idéalement, dans votre application, vous devriez viser 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 de vue et que vous l'avez déplacée vers quelque chose comme redux, ce qui signifie que vous pouvez tester votre vraie logique sans rien afficher. (beaucoup plus facile à tester, plus réutilisable, etc.).

Les inconvénients: 

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

  • Aucun moyen de contrôler manuellement quand un nouveau rendu est nécessaire, car vous ne pouvez pas définir shouldComponentUpdate. Un re-rendu a lieu chaque fois que le composant reçoit de nouveaux accessoires (aucun moyen de comparer peu profond, etc.). À l'avenir, React pourrait optimiser automatiquement les composants sans état. Pour l'instant, vous pouvez utiliser certaines bibliothèques. Comme les composants sans état ne sont que des fonctions, il s’agit du problème classique de la "mémorisation de fonction".

  • 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 de composants:

React utilisait auparavant une PureRenderMixin que vous pouviez attacher à une classe définie à l'aide de la syntaxe React.createClass. Le mixin définirait simplement une shouldComponentUpdate effectuant une comparaison superficielle entre les accessoires suivants et l'état suivant pour vérifier si quelque chose y a changé. Si rien ne change, il n'est pas nécessaire d'effectuer un nouveau rendu.

Si vous souhaitez utiliser la syntaxe ES6, vous ne pouvez pas utiliser mixins. Donc, par commodité, React a introduit une classe PureComponent dont vous pouvez hériter au lieu d’utiliser Component. PureComponent implémente simplement shouldComponentUpdate de la même manière que PureRendererMixin. Il s’agit principalement d’une question de commodité, vous n’aurez donc pas à l’appliquer vous-même, car une comparaison superficielle entre l’état actuel/prochain et les accessoires est probablement le scénario le plus courant pouvant 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 constater, la sortie dépend de props.imageUrl et props.username. Si, dans un composant parent, vous rendez <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" /> avec les mêmes propriétés, React appellera render à chaque fois, même si le résultat obtenu est identique. Rappelez-vous cependant que React implémente la différenciation dom, de sorte que le DOM ne soit pas réellement mis à jour. Néanmoins, effectuer la différenciation peut être coûteux, ce serait donc un gaspillage. 

Si le composant UserAvatar étend PureComponent à la place, une comparaison superficielle est effectuée. Et comme les accessoires et nextProps sont identiques, render ne sera pas appelé du tout.Notes sur la définition de "pur" dans React:.

En général, une "fonction pure" est une fonction qui évalue toujours le même résultat avec la même entrée. La sortie (pour React, c'est ce qui est retourné par la méthode render) ne dépend d'aucun historique/état et elle n'a aucun effet secondaire (opérations qui changent le "monde" en dehors de la fonction).

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

En fait, dans une PureComponent, vous pouvez toujours avoir des effets secondaires lors des méthodes de cycle de vie. Par exemple, vous pouvez envoyer une demande ajax dans componentDidMount ou effectuer un calcul DOM pour ajuster de manière dynamique la hauteur d’un div dans render.

La définition de "composants muets" a un sens plus "pratique" (du moins à ma connaissance): un composant muet "se fait dire" quoi faire par un composant parent via des accessoires, et ne sait pas comment faire mais utilise des accessoires callbacks à la place.

Exemple de "smart" 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> } }

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

In the end I would say that "dumb", "stateless" and "pure" are quite different concepts that can sometimes overlap, but not necessarily, depending mostly on your use case.

280
fabio.sussetto

je ne suis pas un génie, mais de ma compréhension, nous pouvons utiliser chaque composant dans les situations suivantes

  1. Composant sans état - il s’agit du composant qui n’a pas de cycle de vie; ces composants doivent donc être utilisés pour restituer l’élément de répétition du composant parent, tel que le rendu de la liste de texte qui affiche simplement les informations et n’en contient pas. actions à effectuer.

  2. Composante pure - Ce sont les éléments qui ont un cycle de vie et ils retourneront 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’objet spécifiques ne contenant pas d’éléments enfants complexes et permettant d’effectuer des opérations ayant un impact uniquement sur lui-même. Une telle liste d'affichage de cartes d'utilisateur ou de cartes de produits (informations de base sur le produit) et seule l'action que l'utilisateur peut effectuer est de cliquer pour afficher la page de détail ou ajouter au panier.

  3. Composants normaux ou composants complexes - J'ai utilisé le terme composant complexe, car il s’agit généralement de composants de niveau page et comprend un grand nombre de composants enfants. Chaque enfant pouvant se comporter de manière unique, vous ne pouvez donc pas être à 100%. sûr qu'il donnera le même résultat à l'état donné. Comme je le disais d'habitude, ceux-ci devraient être utilisés comme composants de conteneur

19
abhirathore2006
  • React.Component est le composant "normal" par défaut. Vous les déclarez en utilisant le mot clé class et le 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 implémente shouldComponentUpdate() avec une fonction qui effectue une comparaison superficielle de ses props et state. Vous devez utiliser forceUpdate() si vous savez que le composant a des propriétés ou que des données imbriquées d'état ont été modifiées et que vous souhaitez effectuer un nouveau rendu. Ils ne sont donc pas intéressants si vous avez besoin de composants à restituer lorsque des tableaux ou des objets que vous transmettez en tant qu'accessoires ou définis dans votre changement d'état. 

  • Les composants fonctionnels sont ceux qui n'ont pas de fonctions de cycle de vie. Ils sont supposés être apatrides, mais ils sont tellement gentils et propres que nous avons maintenant des crochets (depuis React 16.8) afin que vous puissiez toujours avoir un état. Donc, je suppose qu'ils ne sont que des "composants propres".

0
believesInSanta