OUJOOD.COM
Introduction
Les performances sont cruciales dans toute application React, surtout lorsque celle-ci devient volumineuse ou contient beaucoup de composants imbriqués. React.memo est un outil puissant qui permet de **mémoriser** les rendus de composants fonctionnels afin d'éviter des mises à jour inutiles. Dans ce chapitre, nous allons explorer :
- Qu'est-ce que React.memo ?
- Comment l'utiliser pour optimiser vos composants ?
- Des exemples pratiques pour illustrer son fonctionnement.
1. Qu'est-ce que React.memo ?
React.memo est un **wrapper** autour des composants fonctionnels qui empêche leur re-render si leurs props n'ont pas changé. Il est particulièrement utile pour les composants enfants qui ne doivent pas se mettre à jour lorsqu'un parent change, mais que leurs props restent identiques.
Par défaut, chaque fois qu'un composant parent se met à jour, tous ses enfants sont également re-rendus, même s'ils n'ont pas besoin de changer. React.memo permet de contourner ce comportement en comparant les props actuelles avec les précédentes.
2. Utilisation de React.memo
Voici un exemple simple où nous utilisons React.memo pour éviter des re-renders inutiles :
import React, { useState, memo } from 'react'; // Composant enfant optimisé avec React.memo const EnfantMemo = memo(({ texte }) => { console.log('EnfantMemo rendu'); return <p>{texte}</p>; }); function Parent() { const [compteur, setCompteur] = useState(0); function incrementer() { setCompteur(compteur + 1); } return ( <div> <h1>Compteur : {compteur}</h1> <button onClick={incrementer}>Incrémenter</button> <EnfantMemo texte="Je ne dois pas me re-rendre !" /> </div> ); } ReactDOM.render(<Parent />, document.getElementById('root'));
Explication détaillée :
-
import React, { useState, memo } from 'react';
:- Nous importons React, le hook
useState
pour gérer l'état, etmemo
pour optimiser le composant enfant.
- Nous importons React, le hook
-
const EnfantMemo = memo(({ texte }) => { ... });
:- Ce composant fonctionnel est enveloppé par
memo
, ce qui signifie que React va comparer ses props avant de décider s'il doit être re-rendu. - Si les props n'ont pas changé, React évitera de re-render ce composant, même si le composant parent change.
- Ce composant fonctionnel est enveloppé par
-
console.log('EnfantMemo rendu');
:- Cette ligne affiche un message dans la console chaque fois que le composant est rendu. Cela nous aide à vérifier si React.memo fonctionne correctement.
-
function Parent() { ... }
:- Ce composant parent contient un état appelé
compteur
et un bouton pour l'incrémenter. - Lorsque le bouton est cliqué, le compteur augmente, mais le composant enfant (
EnfantMemo
) ne doit pas se re-rendre car ses props restent inchangées.
- Ce composant parent contient un état appelé
3. Comment React.memo Fonctionne-t-il ?
React.memo compare les props passées à un composant avant de décider s'il doit être re-rendu. Si les props sont identiques (selon une comparaison superficielle), React évite de re-render le composant, ce qui peut considérablement améliorer les performances, surtout dans des applications complexes.
Par défaut, React.memo utilise une **comparaison superficielle** des props. Cela signifie qu'il compare uniquement les valeurs primitives (chaînes, nombres, booléens) et les références des objets/tableaux. Si vous avez besoin d'une logique personnalisée pour comparer les props, vous pouvez passer une fonction comme deuxième argument à React.memo.
Exemple avec Comparaison Personnalisée
Voici un exemple où nous définissons une fonction personnalisée pour comparer les props :
import React, { useState, memo } from 'react'; // Comparaison personnalisée const areEqual = (prevProps, nextProps) => { return prevProps.texte === nextProps.texte; }; // Composant enfant optimisé avec React.memo et comparaison personnalisée const EnfantMemo = memo(({ texte }) => { console.log('EnfantMemo rendu'); return <p>{texte}</p>; }, areEqual); function Parent() { const [compteur, setCompteur] = useState(0); function incrementer() { setCompteur(compteur + 1); } return ( <div> <h1>Compteur : {compteur}</h1> <button onClick={incrementer}>Incrémenter</button> <EnfantMemo texte="Je ne dois pas me re-rendre !" /> </div> ); } ReactDOM.render(<Parent />, document.getElementById('root'));
Explication détaillée :
-
const areEqual = (prevProps, nextProps) => { ... }
:- Cette fonction personnalisée compare les props précédentes (
prevProps
) avec les nouvelles props (nextProps
) pour décider si le composant doit être re-rendu. - Ici, nous vérifions simplement si la prop
texte
a changé.
- Cette fonction personnalisée compare les props précédentes (
-
const EnfantMemo = memo(({ texte }) => { ... }, areEqual);
:- Le composant
EnfantMemo
est enveloppé parmemo
, avec notre fonctionareEqual
comme deuxième argument. - Cela garantit que React utilisera notre logique personnalisée pour décider si le composant doit être re-rendu.
- Le composant
-
function Parent() { ... }
:- Ce composant parent contient un état appelé
compteur
et un bouton pour l'incrémenter. - Même si le compteur change, le composant
EnfantMemo
ne sera pas re-rendu tant que sa proptexte
reste inchangée.
- Ce composant parent contient un état appelé
4. Limitations de React.memo
Bien que React.memo soit un outil puissant, il présente certaines limitations importantes à connaître :
- Comparaison Superficielle : React.memo ne détecte pas les changements profonds dans les objets ou tableaux. Par exemple, si une prop est un objet complexe, React.memo ne pourra pas savoir si son contenu a changé.
- Pas Adapté aux Composants Basés sur des Classes : React.memo ne peut être utilisé qu'avec des composants fonctionnels. Pour les composants basés sur des classes, utilisez plutôt
PureComponent
. - Coût de Comparaison : Si la comparaison des props est trop coûteuse (par exemple, avec de grandes structures de données), cela pourrait annuler les gains de performance apportés par React.memo.
5. Exemple Concret : Optimisation d'une Liste
Imaginons que vous avez une liste de plusieurs éléments, mais que certains d'entre eux ne changent jamais. Vous pouvez utiliser React.memo pour éviter de re-render ces éléments inutilement. Voici un exemple :
import React, { useState, memo } from 'react'; // Composant enfant optimisé avec React.memo const ElementListe = memo(({ nom }) => { console.log(`ElementListe "${nom}" rendu`); return <li>{nom}</li>; }); function Liste() { const [nouveauNom, setNouveauNom] = useState(''); const [noms, setNoms] = useState(['Alice', 'Bob', 'Charlie']); function ajouterNom() { if (nouveauNom.trim() !== '') { setNoms([...noms, nouveauNom]); setNouveauNom(''); } } return ( <div> <input type="text" value={nouveauNom} onChange={(e) => setNouveauNom(e.target.value)} placeholder="Ajoutez un nom..." /> <button onClick={ajouterNom}>Ajouter</button> <ul> {noms.map((nom, index) => ( <ElementListe key={index} nom={nom} /> ))} </ul> </div> ); } ReactDOM.render(<Liste />, document.getElementById('root'));
Explication détaillée :
-
const ElementListe = memo(({ nom }) => { ... });
:- Ce composant représente un élément de la liste. Il est enveloppé par
memo
pour éviter des re-renders inutiles.
- Ce composant représente un élément de la liste. Il est enveloppé par
-
function Liste() { ... }
:- Ce composant parent gère une liste de noms via l'état
noms
. - Un champ de saisie permet d'ajouter de nouveaux noms à la liste.
- Ce composant parent gère une liste de noms via l'état
-
{noms.map((nom, index) => (...))}
:- Itère sur le tableau
noms
pour générer dynamiquement des éléments<ElementListe>
. - Chaque élément est assigné une clé unique via
key={index}
.
- Itère sur le tableau
-
console.log(`ElementListe "${nom}" rendu`);
:- Affiche un message dans la console chaque fois qu'un élément de la liste est rendu.
- Grâce à React.memo, seuls les nouveaux éléments seront re-rendus lorsqu'un nom est ajouté.
6. Meilleures Pratiques pour Utiliser React.memo
Pour tirer pleinement parti de React.memo dans vos projets, voici quelques conseils :
- Utilisez-le avec parcimonie : Ne surutilisez pas React.memo, car cela pourrait entraîner des coûts supplémentaires liés aux comparaisons de props.
- Privilégiez les composants fonctionnels : React.memo ne peut être utilisé qu'avec des composants fonctionnels. Pour les composants basés sur des classes, utilisez
PureComponent
. - Testez les performances : Avant d'appliquer React.memo, mesurez les performances de votre application pour vérifier si cela est nécessaire.
Conclusion
Vous avez maintenant appris à utiliser React.memo pour optimiser la performance de vos composants React. En minimisant les re-renders inutiles, vous pouvez rendre vos applications plus rapides et efficaces, surtout dans des scénarios complexes impliquant de nombreux composants imbriqués.
Prochain chapitre : Styling CSS avec React