Amélioration de la fonctionnalité des composants React avec React.memo ()

Nous vous présentons une traduction de l'article de Chidume Nnamdi, qui a été publié sur blog.bitsrc.io. Si vous voulez savoir comment éviter le rendu inutile et comment les nouveaux outils sont utiles dans React, bienvenue à cat.



L'équipe React.js travaille dur pour faire fonctionner React le plus rapidement possible. Pour permettre aux développeurs d'accélérer leurs applications React, les outils suivants y ont été ajoutés:

  • React.lazy et Suspense pour un chargement retardé des composants;
  • Composant pur
  • les hooks du cycle de vie devraientComponentUpdate (...) {...}.

Dans cet article, nous considérerons, entre autres, un autre outil d'optimisation ajouté dans React v16.6 pour accélérer les fonctions des composants - React.memo .

Conseil: utilisez Bit pour installer et partager des composants React. Utilisez vos composants pour créer de nouvelles applications et partagez-les avec l'équipe pour accélérer les choses. Essayez-le!



Rendu supplémentaire


Dans React, chaque composant correspond à une unité de vue. Les composants ont également des états. Lorsque la valeur d'état change en raison des actions de l'utilisateur, le composant se rend compte qu'un nouveau dessin est nécessaire. Le composant React peut être redessiné un certain nombre de fois. Dans certains cas, cela est nécessaire, mais le plus souvent, vous pouvez vous passer d'un moteur de rendu, d'autant plus que cela ralentit considérablement l'application.

Considérez le composant suivant:

import React from 'react'; class TestC extends React.Component { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate') } componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate') } render() { return ( <div > {this.state.count} <button onClick={()=>this.setState({count: 1})}>Click Me</button> </div> ); } } export default TestC; 

La valeur initiale de l'état {count: 0} est 0. Si vous cliquez sur le bouton Click me, l'état de count deviendra 1. Sur notre écran, 0 passera également à 1. Mais si nous cliquons à nouveau sur le bouton, les problèmes commencent: le composant ne doit pas être redessiné, car il la condition n'a pas changé. La valeur du compteur «à» est 1, la nouvelle valeur est également une, ce qui signifie qu'il n'est pas nécessaire de mettre à jour le DOM.

Pour voir la mise à jour de notre TestC, dans lequel le même état est défini deux fois, j'ai ajouté deux méthodes de cycle de vie. React démarre le cycle componentWillUpdate lorsque le composant est mis à jour / redessiné en raison d'un changement d'état. Le cycle componentdidUpdate React démarre lorsqu'un composant s'affiche correctement.

Si nous lançons le composant dans le navigateur et essayons de cliquer plusieurs fois sur le bouton Click me, nous obtenons le résultat suivant:



La répétition de l'entrée componentWillUpdate dans notre console indique que le composant est redessiné même lorsque l'état ne change pas. Ceci est un rendu supplémentaire.

Composant pur / shouldComponentUpdate


Le hook de cycle de vie shouldComponentUpdate permet d'éviter le rendu inutile dans les composants React.

React lance la méthode shouldComponentUpdate au début du rendu des composants et reçoit un feu vert de cette méthode pour continuer le processus ou un signal indiquant que le processus est inhibé .

Laissez notre shouldComponentUpdate ressembler à ceci:

 shouldComponentUpdate(nextProps, nextState) { return true } 

  • nextProps : la prochaine valeur d' props que le composant recevra;
  • nextState : la prochaine valeur d' state que le composant recevra.

Nous permettons donc à React de rendre le composant car la valeur de retour est true .

Supposons que nous écrivions ce qui suit:

 shouldComponentUpdate(nextProps, nextState) { return false } 

Dans ce cas, nous interdisons à React de restituer le composant, car false retourné.
De ce qui précède, il s'ensuit que pour rendre le composant, nous devons retourner true . Maintenant, nous pouvons réécrire le composant TestC comme suit:

 import React from 'react'; class TestC extends React.Component { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate') } componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate') } shouldComponentUpdate(nextProps, nextState) { if (this.state.count === nextState.count) { return false } return true } render() { return ( <div> { this.state.count } <button onClick = { () => this.setState({ count: 1 }) }> Click Me </button> </div> ); } } export default TestC; 

Nous avons ajouté un hook shouldComponentUpdate au composant TestC. La valeur de count dans l'objet d'état actuel this.state.count comparée à la valeur de count dans l'objet d'état suivant nextState.count . S'ils sont égaux === , le redessin ne se produit pas et false renvoyé. S'ils ne sont pas égaux, true renvoyé et un moteur de rendu est lancé pour afficher la nouvelle valeur.

Si nous testons le code dans un navigateur, nous verrons un résultat familier:



Mais en cliquant plusieurs fois sur le bouton Click Me , tout ce que nous voyons est le suivant (affiché une seule fois!):

componentWillUpdate
componentDidUpdate




Vous pouvez modifier l'état du composant TestC dans l'onglet React DevTools. Cliquez sur l'onglet Réagir, sélectionnez TestC à droite, et vous verrez la valeur d'état du compteur:



Cette valeur peut être modifiée. Cliquez sur le texte du compteur, tapez 2 et appuyez sur Entrée.



L'état de comptage va changer, et dans la console, nous verrons:

 componentWillUpdate componentDidUpdate componentWillUpdate componentDidUpdate 



La valeur précédente était 1 et la nouvelle était 2, donc un nouveau tracé était nécessaire.
Passons au composant pur .

Pure Component est apparu dans React dans la version v15.5. Il est utilisé pour comparer les valeurs par défaut ( change detection ). En utilisant extend React.PureComponent , vous n'avez pas besoin d'ajouter la méthode de cycle de vie shouldComponentUpdate aux composants: le suivi des modifications se fait tout seul.

Ajoutez un PureComponent au composant TestC.

 import React from 'react'; class TestC extends React.PureComponent { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate') } componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate') } /*shouldComponentUpdate(nextProps, nextState) { if (this.state.count === nextState.count) { return false } return true }*/ render() { return ( <div> { this.state.count } <button onClick = { () => this.setState({ count: 1 }) }> Click Me </button> </div > ); } } export default TestC; 

Comme vous pouvez le voir, nous avons shouldComponentUpdate dans un commentaire. Nous n'en avons plus besoin: tout le travail est fait par React.PureComponent .

En redémarrant le navigateur pour tester la nouvelle solution, et en cliquant plusieurs fois sur le bouton Click Me , nous obtenons:





Comme vous pouvez le voir, un seul component*Update Entrée de component*Update est apparu dans la console.

Après avoir vu comment travailler dans React avec redessiner dans les classes de composants d'ES6, nous passons aux fonctions des composants. Comment obtenir les mêmes résultats avec eux?

Composants de fonction


Nous savons déjà comment optimiser le travail avec les classes en utilisant le composant pur et la shouldComponentUpdate cycle de vie shouldComponentUpdate . Personne ne prétend que les composants de classe sont les principaux composants de React, mais vous pouvez utiliser des fonctions comme composants.

 function TestC(props) { return ( <div> I am a functional component </div> ) } 

Il est important de se rappeler que les composants de fonction, contrairement aux composants de classe, n'ont pas d'état (bien que maintenant que les hooks useState soient useState , cela peut être discuté), ce qui signifie que nous ne pouvons pas configurer leur redessin. Les méthodes de cycle de vie que nous avons utilisées lorsque nous travaillions avec des classes ne sont pas disponibles ici. Si nous pouvons ajouter des crochets de cycle de vie aux composants de fonction, nous pouvons ajouter la méthode shouldComponentUpdate pour indiquer à React qu'un rendu de fonction est nécessaire. (L'auteur a peut-être fait une erreur factuelle dans la dernière phrase. - Éd. Approx.) Et, bien sûr, nous ne pouvons pas utiliser l' extend React.PureComponent .

Nous transformons notre classe de composants ES6 TestC en fonction de composant.

 import React from 'react'; const TestC = (props) => { console.log(`Rendering TestC :` props) return ( <div> {props.count} </div> ) } export default TestC; // App.js <TestC count={5} /> 

Après le rendu dans la console, nous voyons l' Rendering TestC :5 .



Ouvrez DevTools et cliquez sur l'onglet React. Ici, nous allons essayer de changer la valeur des propriétés du composant TestC. Sélectionnez TestC, et les propriétés du compteur avec toutes les propriétés et valeurs de TestC s'ouvriront sur la droite. Nous ne voyons que le compteur avec la valeur actuelle de 5.

Cliquez sur le chiffre 5 pour modifier la valeur. Une fenêtre de saisie apparaîtra à la place.



Si nous modifions la valeur numérique et appuyons sur Entrée, les propriétés du composant changeront en fonction de la valeur que nous avons entrée. Supposons à 45.



Accédez à l'onglet Console.



Le composant TestC a été redessiné car la valeur précédente de 5 est devenue la valeur actuelle - 45. Revenez à l'onglet React et changez la valeur en 45, puis revenez à la console.



Comme vous pouvez le voir, le composant est redessiné, bien que les valeurs précédente et nouvelle soient les mêmes. :(

Comment gérer un moteur de rendu?

Solution: React.memo ()


React.memo() est une nouvelle fonctionnalité introduite dans React v16.6. Son principe de fonctionnement est similaire au principe de React.PureComponent : aide à la gestion du redessin des fonctions des composants. React.memo(...) pour les composants de classe est React.PureComponent pour les composants de fonction.

Comment travailler avec React.memo (...)?
Assez simple. Disons que nous avons une fonction composante.

 const Funcomponent = ()=> { return ( <div> Hiya!! I am a Funtional component </div> ) } 

Il suffit de passer FuncComponent comme argument à la fonction React.memo.

 const Funcomponent = ()=> { return ( <div> Hiya!! I am a Funtional component </div> ) } const MemodFuncComponent = React.memo(FunComponent) 

React.memo renvoie un MemodFuncComponent purified MemodFuncComponent . C'est ce que nous allons dessiner dans le balisage JSX. Lorsque les propriétés et l'état d'un composant changent, React compare les propriétés et les états précédents et actuels du composant. Et seulement s'ils ne sont pas identiques, la fonction composant est redessinée.

Appliquez cela au composant de fonction TestC.

 let TestC = (props) => { console.log('Rendering TestC :', props) return ( <div> { props.count } </> ) } TestC = React.memo(TestC); 

Ouvrez un navigateur et téléchargez l'application. Ouvrez DevTools et accédez à l'onglet React. Sélectionnez <Memo(TestC)> .

Si dans le bloc de droite nous changeons les propriétés du compteur à 89, l'application sera redessinée.



Si nous changeons la valeur par la précédente, 89, alors ...



Il n'y aura pas de refonte!

Gloire à React.memo (...)! :)

Sans utiliser React.memo(...) dans notre premier exemple, la fonction du composant TestC est redessinée même lorsque la valeur précédente devient identique. Maintenant, grâce à React.memo(...) , nous pouvons éviter un rendu inutile des fonctions des composants.

Conclusion


  • Passons en revue la liste?
  • React.PureComponent - argent;
  • React.memo(...) - or;
  • React.PureComponent fonctionne avec les classes ES6;
  • React.memo(...) fonctionne avec des fonctions;
  • React.PureComponent optimise le redessin des classes ES6;
  • React.memo(...) optimise le redessin des fonctions;
  • l'optimisation des fonctionnalités est une excellente idée;
  • React ne sera plus jamais le même.

Si vous avez des questions sur l'article ou des informations supplémentaires, des changements ou des objections, n'hésitez pas à m'écrire des commentaires, des courriels ou des messages privés.

Je vous remercie!

Source: https://habr.com/ru/post/fr442116/


All Articles