logo oujood
🔍

React Memo : Optimisez la Performance de Vos Composants

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 :

📋 Copier le code

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 :

  1. import React, { useState, memo } from 'react'; :
    • Nous importons React, le hook useState pour gérer l'état, et memo pour optimiser le composant enfant.
  2. 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.
  3. 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.
  4. 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.

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 :

📋 Copier le code

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 :

  1. 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é.
  2. const EnfantMemo = memo(({ texte }) => { ... }, areEqual); :
    • Le composant EnfantMemo est enveloppé par memo, avec notre fonction areEqual comme deuxième argument.
    • Cela garantit que React utilisera notre logique personnalisée pour décider si le composant doit être re-rendu.
  3. 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 prop texte reste inchangée.

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 :

📋 Copier le code

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 :

  1. 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.
  2. 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.
  3. {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}.
  4. 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