Optimisation Granulaire avec React.memo et useCallback
9 min de lecture
1. Introduction à React.memo et useCallback
Avec la croissance constante des applications React en taille et en complexité, l'optimisation des performances est devenue primordiale. Deux outils efficaces mais parfois sous-exploités sont React.memo
et useCallback
.
1.1 Principe et Usage de React.memo
Le React.memo
est une fonction d'ordre supérieur (HOF) qui mémorise le résultat de rendu d'un composant si ses accessoires sont les mêmes qu'au rendu précédent. Ceci permet d'éviter des rendus inutiles pour améliorer les performances.
Dans cet exemple, le composant MyComponent
ne sera pas re-rendu si les accessoires restent les mêmes.
1.2 Principe et Usage de useCallback
Le useCallback
est un crochet qui peut être utilisé pour mémoriser une fonction de rappel entre les rendus. Il est particulièrement utile lorsque vous passez des fonctions de rappel à des composants fortement imbriqués.
Dans ce cas, la fonction de rappel n'est recréée que si les valeurs de a
ou b
changent.
1.3 Les bénéfices potentiels pour les Performances
L'usage de React.memo
et useCallback
permet de réduire le travail nécessaire pour re-rendre des composants. Cela peut conduire à une réduction significative du temps de rendu et améliorer la fluidité de l'interaction utilisateur.
Note : Ces techniques d'optimisation ne sont pas une panacée. L'usage de
React.memo
etuseCallback
peut avoir un coût car elles ajoutent une surcharge de mémorisation. Il est donc recommandé de mesurer l'impact sur les performances avant et après leur mise en place pour s'assurer de leur utilité.
En conclusion, React.memo
et useCallback
peuvent être des outils précieux pour l'optimisation des performances dans les applications React. Cependant, leur utilité réelle dépend des circonstances spécifiques et d'une bonne compréhension de leur fonctionnement. Les sections suivantes approfondiront ces aspects et permettront une utilisation plus appropriée de ces outils.
2. Approfondissement sur React.memo
2.1 Comment React.memo fonctionne-t-il ?
React.memo
est une fonction d'ordre supérieur. Elle prend un composant et retourne un composant mémorisé qui peut prévenir le rendu inutile en comparant les accessoires.
Remarque : Par défaut,
React.memo
comparer shagnostiquement chaque champ de l'objet accessoires comme le feraitObject.is
. Cependant, une fonction de comparaison personnalisée peut être fournie en second argument àReact.memo
pour changer cette comparaison par défaut.
L'utilisation classique est la suivante :
Dans cet exemple, React.memo
empêche le rendu de ComponentName
à moins que ses accessoires n'aient changé.
2.2 Exemples d'usage de React.memo
-
Prévenir le re-rendu de composants non concernés par une mise à jour d'état: Parfois, une mise à jour d'état d'un composant parent entraîne le rendu des composants enfants même si les accessoires reçus par les enfants n'ont pas changé.
-
Gestion des tables de données volumineuses: Si une application contient une table de données énorme avec des dizaines de colonnes et des milliers de lignes, rendre toutes ces données à chaque fois peut ralentir l'application.
React.memo
peut rendre uniquement les lignes ou les cellules modifiées et diminuer ainsi le temps d'affichage. -
Optimisation des listes: S'il existe un composant qui rend une longue liste, chaque modification mineure de cette liste entraîne le rendu de l'ensemble de la liste. L'emploi de
React.memo
peut aider à rendre uniquement les éléments modifiés de la liste.
Important : l'utilisation de
React.memo
nécessite un benchmarking avant et après l'optimisation pour vérifier si elle apporte les améliorations de performance attendues.
2.3 Quand utiliser React.memo
React.memo
devrait être utilisé quand :
- Le rendu d'un composant est coûteux et le composant est fréquemment re-rendu avec les mêmes accessoires.
- Un composant est re-rendu par des changements d'état qui n'affectent pas effectivement le composant.
- Une large partie de l'arbre de composants n'a pas besoin d'être re-rendu du fait des changements d'état ou des accessoires de quelques composants.
À savoir : l'utilisation de
React.memo
peut également avoir des inconvénients. Il peut introduire des frais généraux supplémentaires pour le processus de comparaison des accessoires. Par conséquent, son utilisation devrait être soigneusement considérée.
3. Approfondissement sur useCallback
3.1 Comment useCallback fonctionne-t-il ?
useCallback
est un Hook qui permet de mémoriser une fonction de rappel entre les rendus d'un composant. La syntaxe de useCallback
est la suivante :
Dans cet exemple, la fonction de rappel est mémorisée jusqu'à ce que a
ou b
change.
3.2 Exemples d'usage de useCallback
Voici quelques cas où useCallback
peut se révéler bénéfique :
- Passation des fonctions de rappel à des composants fortement imbriqués : Si un composant parent fournit une fonction de rappel à un composant enfant à l'aide d'accessoires et que cette fonction ne doit pas changer entre les rendus,
useCallback
peut être utilisé pour mémoriser la fonction de rappel. - Rendu coûteux : Si le rendu d'un composant est coûteux, et qu'une fonction à l'intérieur de ce composant peut déclencher ce rendu, utiliser
useCallback
pour mémoriser la fonction peut aider à réduire le coût. - Dépendances des effets : Parfois, une fonction est une dépendance d'un effet (
useEffect
,useMemo
, etc.). Pour éviter que l'effet ne se déclenche chaque fois que le composant est rendu, on peut utiliseruseCallback
pour mémoriser la fonction.
3.3 Quand utiliser useCallback ?
useCallback
devrait être utilisé lorsque :
- Vous passez une fonction en tant qu'accessoire à un composant enfant pour éviter les rendus inutiles de ce composant.
- Vous utilisez une fonction à l'intérieur d'un effet (
useEffect
,useMemo
, etc.) comme une dépendance et que vous ne voulez pas que l'effet se déclenche à chaque rendu.
Important : Comme pour
React.memo
, le coût de la mémorisation peut parfois annuler les bénéfices apportés paruseCallback
, en particulier si la création de la fonction de rappel est peu coûteuse et qu'elle ne déclenche pas un grand nombre de rendus en aval. L'utilisation deuseCallback
devrait donc être mesurée avec soin.
4. Éviter les pièges courants
4.1 Comprendre l'égalité référentielle en JavaScript
L'égalité référentielle est un concept crucial à comprendre lors de l'utilisation de React.memo
et useCallback
. Cette notion se base sur l'identité des objets et non leur structure.
En JavaScript, deux objets ou deux tableaux sont considérés comme égaux uniquement s'ils font référence au même objet, même si leurs contenus sont identiques. Par exemple :
Dans cet exemple, malgré le fait que a
et b
ont le même contenu, ils sont considérés comme différents aux yeux de JavaScript car ils ne font pas référence au même objet. Le concept d'égalité référentielle est essentiel pour comprendre comment React.memo
et useCallback
déterminent si le re-rendering est nécessaire ou non.
4.2 Les erreurs courantes et comment les éviter
-
Création d'objets ou de tableaux dans les accessoires : Ceci peut déclencher des re-renderings inutiles car chaque objet ou tableau créé a une nouvelle référence.
Solution : Stocker ces objets ou tableaux dans l'état du composant ou utiliser
useMemo
pour une création conditionnelle. -
Utilisation de fonctions dans les accessoires : Des situations comparables se produisent avec les fonctions. Une nouvelle référence est créée à chaque rendu, ce qui déclenche inutilement un re-rendering.
Solution : Utiliser
useCallback
pour mémoriser les fonctions. -
Manque de dépendances dans le tableau de dépendances de
useCallback
: Ce cas peut entraîner des comportements inattendus car la fonction pourrait ne pas utiliser les valeurs les plus récentes des variables.Solution : S'assurer que toutes les variables utilisées dans la fonction de rappel sont incluses dans le tableau de dépendances.
Attention : Le coût de la mémorisation avec
useCallback
ouReact.memo
peut annuler les économies de performances dans certains cas, notamment si les fonctions ou composants sont peu coûteux à générer et ne déclenchent pas de nombreux re-rendering. Leur utilisation doit toujours être mesurée avec soin.
5. Études de cas et solutions
5.1 Cas d'une liste de Composants
Imaginons que nous avons une liste de tâches avec chaque tâche représentée par un composant Task
. Chaque fois qu'une tâche est mise à jour, tous les composants Task
sont re-rendus, ce qui peut conduire à des problèmes de performance.
Solution : Utiliser React.memo
pour éviter les re-rendus inutiles. Chaque composant Task
sera re-rendu seulement si les props qu'il reçoit changent. C'est-à-dire, si la Task
en question est mise à jour.
Exemple de code :
Ainsi, seules les tâches modifiées seront re-rendues. Il est important de noter que la fonction updateTask
doit être mémorisée avec useCallback
pour garantir une référence constante et éviter les re-rendus inutiles.
5.2 Cas d'un Composant avec de nombreux props
Supposons que nous avons un composant UserProfile
qui reçoit de nombreux props. Chaque fois qu'un seul prop est modifié, le composant entier est re-rendu.
Solution : Créer des composants plus petits pour chaque prop et les mémoriser en utilisant React.memo
. Chaque composant ne sera re-rendu que si le prop qu'il reçoit est modifié.
Voici un exemple de comment structurer le code :
Dans cet exemple, chaque sous-composant ne sera re-rendu que si le prop spécifique associé à ce composant est modifié. Cela permet de réduire le nombre de re-rendus inutiles lorsque les props changent.
Remarque : Il est important de noter que la création de nombreux sous-composants et l'utilisation de
React.memo
peut introduire une surcharge de mémorisation et augmenter la complexité du code. Par conséquent, cette méthode doit être utilisée judicieusement et en fonction des exigences spécifiques du projet.
6. Comparaison avec d'autres techniques d'optimisation
6.1 React.PureComponent et shouldComponentUpdate
React.PureComponent
effectue une comparaison superficielle de chaque champ de l'objet des accessoires et de l'état. Si une modification se produit à n'importe quel niveau de l'objet, le composant sera re-rendu. React.memo
offre une flexibilité supplémentaire en ce sens qu'il permet l'usage d'une fonction de comparaison personnalisée.
shouldComponentUpdate
est une méthode de cycle de vie des composants de classe qui permet un contrôle granulaire sur le processus de re-rendu. Elle exige cependant une programmation plus délicate et une connaissance approfondie du composant. De plus, elle n'est pas utilisable avec les composants fonctionnels utilisés dans Hooks.
React.memo | React.PureComponent | shouldComponentUpdate | |
---|---|---|---|
Flexibilité de comparaison | Haute (fonction de comparaison personnalisable) | Modérée (comparaison superficielle des accessoires et de l'état) | Haute (comparaison personnalisable) |
Facilité d'usage | Haute | Mmodérée | Faible (requiert plus de programmation) |
Compatibilité avec Hooks | Oui | Non | Non |
Note : Les performances dépendent spécifiquement du cas. Il est recommandé de procéder à un benchmarking avant et après l'optimisation pour confirmer l'efficacité.
6.2 React.useMemo et React.useEffect
useMemo
est similaire à useCallback
, mais pour les valeurs calculées coûteuses. useMemo
récupère le résultat du calcul entre les rendus jusqu'à ce que les dépendances changent.
useEffect
est utilisé pour effectuer des effets secondaires dans les composants et n'offre pas de mémorisation. Cependant, il peut avoir des dépendances (comme une fonction provenant de useCallback
), et il se déclenchera seulement quand ces dépendances changent.
useCallback | useMemo | useEffect | |
---|---|---|---|
Objectif principal | Mémoriser les fonctions de rappel | Mémoriser les valeurs calculées | Exécuter les effets secondaires |
Rendu basé sur des dépendances | Oui | Oui | Oui |
Remarque : Il convient de noter que l'usage de ces Hooks peut également avoir un coût, principalement dû à la surcharge de mémorisation. Leur utilisation devrait donc être dépendante du cas d'utilisation (source).
7. Conseils pour la mise en œuvre pratique
7.1 Comment choisir la bonne stratégie?
Pour parvenir à une optimisation granulaire, il est nécessaire de comprendre votre système et de bien bénéficier de ces techniques d'optimisation. Quelques facteurs à considérer incluent:
-
Le type de composants : Les composants de classe sont plus optimisés avec
shouldComponentUpdate
ouReact.PureComponent
, tandis que les composants fonctionnels fonctionnent bien avecReact.memo
,useCallback
ouuseMemo
. -
La fréquence des mises à jour : Pour les composants mis à jour à une fréquence relativement basse, les techniques d'optimisation peuvent ne pas apporter d'amélioration de performance notable et peuvent même ajouter une surcharge inutile. Cependant, pour des composants à mise à jour fréquente, ces techniques peuvent aider à améliorer les performances.
-
Le nombre de composants enfants : Les composants ayant un grand nombre de composants enfants peuvent bénéficier plus de
useCallback
etReact.memo
pour éviter les re-rendus inutiles.
7.2 Mesurer l'impact sur les performances
Avant et après la mise en œuvre de ces techniques, il est essentiel d'évaluer l'efficacité de l'optimisation. Les outils tels que le Profiling add-on pour React DevTools peuvent fournir des informations précieuses sur les temps de rendu des composants.
7.3 Comment combiner ces méthodes pour un effet maximum ?
Il n'y a pas de solution unique à tous les scénarios d'optimisation. L'efficacité de React.memo
, useCallback
, et autres techniques dépend des spécificités du projet. Les bonnes pratiques comprennent:
-
Combiner
React.memo
etuseCallback
: Quand vous passez des fonctions de rappel à un composant enfant via les accessoires, utiliserReact.memo
sur le composant enfant etuseCallback
pour mémoriser la fonction de rappel. -
Utiliser
useMemo
avec des objets coûteux : Quand vous avez des objets coûteux à calculer et que vous voulez éviter de les recréer à chaque rendu,useMemo
est pratique. -
Employer
useEffect
judicieusement :useEffect
est utile pour gérer les effets secondaires et peut être optimisé en ajustant son tableau de dépendances.
En conclusion, ces techniques d'optimisation fournissent des outils puissants pour améliorer les performances des applications React. Cependant, la clé est de comprendre les mécanismes sous-jacents, de mesurer leur impact sur les performances, et d'effectuer des ajustements basés sur les résultats spécifiques du projet.
8. Réflexions finales sur l'optimisation des performances
8.1 L'optimisation précoce est le mal
On dit souvent en développement logiciel que l'optimisation prématurée est la cause de tous les maux. C'est particulièrement vrai en React. Avant d'optimiser quoi que ce soit, assurez-vous que vous avez réellement un problème de performance. Utilisez des profilers pour identifier les goulots d'étranglement de performance spécifiques. Ensuite, choisissez les techniques d'optimisation appropriées pour ces problèmes spécifiques.
8.2 Le rôle de l'évolution des pratiques
L'écosystème JavaScript et React évolue rapidement. Les meilleures pratiques d'aujourd'hui peuvent devenir obsolètes demain à mesure que de nouveaux outils et techniques sont introduits. Il est important de se tenir informé et de continuer à apprendre.
5.0 (33 notes)