Comment décidez-vous, comment choisissez-vous entre ces trois éléments en fonction de l'objectif / de la taille / des accessoires / du comportement de nos composants?
L'extension à partir React.PureComponent
ou à partir React.Component
d'une shouldComponentUpdate
méthode personnalisée a des implications sur les performances. L'utilisation de composants fonctionnels sans état 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. De cette façon, vous êtes sûr qu'ils sont découplés de la logique réelle de l'application, qu'ils sont extrêmement faciles à tester et qu'ils n'ont pas d'effets secondaires inattendus. L'exception est si, pour une raison quelconque, vous en avez beaucoup ou si vous avez vraiment besoin d'optimiser leur méthode de rendu (comme vous ne pouvez pas définir shouldComponentUpdate
pour un composant fonctionnel sans état).
Étendez PureComponent
si vous savez que votre sortie dépend de simples accessoires / état («simple» signifie pas de structures de données imbriquées, car PureComponent effectue une comparaison superficielle) ET vous avez besoin / pouvez obtenir des améliorations de performances.
Étendez Component
et implémentez les vôtres shouldComponentUpdate
si vous avez besoin de gains de performances en exécutant une logique de comparaison personnalisée entre les accessoires et l'état suivant / actuel. 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, implémenter les vôtres shouldComponentUpdate
ou étendre à partir de PureComponent
sont des optimisations, et comme d'habitude, vous ne devriez commencer à vous pencher sur cela que 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 une fois que 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 lorsqu'ils sont réellement gênants.
Plus de détails
Composants fonctionnels sans état:
Celles-ci sont définies simplement à l'aide d'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 accessoires donnés en entrée de cette fonction.
Avantages:
La manière la plus simple possible de définir un composant dans React. Si vous n'avez besoin de gérer aucun état, pourquoi vous soucier 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 dépend uniquement de l'entrée (et non de l'historique des exécutions précédentes).
Idéalement, dans votre application, vous devriez viser à avoir autant de composants sans état que possible, car cela signifie normalement que vous avez déplacé votre logique en dehors de la couche de vue et l'avez déplacée vers quelque chose comme redux, ce qui signifie que vous pouvez tester votre logique réelle sans avoir à rendre quoi que ce soit. (beaucoup plus facile à tester, plus réutilisable, etc.).
Les inconvénients:
Aucune méthode de cycle de vie. Vous n'avez pas de moyen de définir componentDidMount
et d'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 le moment où un nouveau rendu est nécessaire, car vous ne pouvez pas définir shouldComponentUpdate
. Un nouveau rendu se produit chaque fois que le composant reçoit de nouveaux accessoires (aucun moyen de comparaison superficielle, etc.). À l'avenir, React pourrait optimiser automatiquement les composants sans état, pour l'instant, vous pouvez utiliser certaines bibliothèques. Puisque les composants sans état ne sont que des fonctions, c'est fondamentalement le problème classique de la "mémorisation de 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 un que PureRenderMixin
vous pouviez attacher à une classe définie à l'aide de la React.createClass
syntaxe. Le mixin définirait simplement une shouldComponentUpdate
comparaison superficielle entre les accessoires suivants et l'état suivant pour vérifier si quelque chose 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 les mixins. Donc, pour plus de commodité, React a introduit une PureComponent
classe dont vous pouvez hériter au lieu d'utiliser Component
. PureComponent
implémente simplement shouldComponentUpdate
de la même manière que le PureRendererMixin
. C'est surtout une chose de commodité, vous n'avez donc 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 donner des gains de performances rapides.
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
et props.username
. Si dans un composant parent vous effectuez <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />
le rendu avec les mêmes accessoires, React appellerait à render
chaque fois, même si la sortie serait exactement la même. Souvenez-vous cependant que React implémente dom diffing, donc le DOM ne serait pas réellement mis à jour. Pourtant, effectuer la différence de dom peut être coûteux, donc dans ce scénario, ce serait un gaspillage.
Si le UserAvatar
composant s'étend à la PureComponent
place, une comparaison superficielle est effectuée. Et comme les props et nextProps sont les mêmes, render
ne seront pas du tout appelés.
Remarques 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 render
méthode) ne dépend d'aucun historique / état et 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 a 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 componentDidMount
ou effectuer un calcul DOM pour ajuster dynamiquement la hauteur d'un div à l'intérieur render
.
La définition des "composants stupides" a une signification plus "pratique" (du moins dans ma compréhension): un composant stupide "se fait dire" quoi faire par un composant parent via des accessoires, et ne sait pas comment faire les choses mais utilise des accessoires rappels à 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>
}
}
Exemple de "stupide" 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 "stupide", "apatride" et "pur" sont des concepts assez différents qui peuvent parfois se chevaucher, mais pas nécessairement, en fonction principalement de votre cas d'utilisation.