logo oujood
🔍

useMemo : Mémorisez des Valeurs Calculées pour Optimiser vos Performances

OUJOOD.COM

Introduction

Le Hook `useMemo` est utilisé pour mémoriser des valeurs calculées afin d'éviter des recalculs inutiles lors des re-renders. Cela peut considérablement améliorer les performances de vos applications React, surtout lorsque ces calculs sont coûteux ou impliquent des données complexes.

Dans ce chapitre, nous allons explorer :

  • Qu'est-ce que `useMemo` ?
  • Comment créer et utiliser une valeur mémorisée.
  • Des exemples pratiques pour illustrer son fonctionnement.

1. Qu'est-ce que `useMemo` ?

`useMemo` est un Hook React qui renvoie une valeur mémorisée. Cette valeur ne sera recalculée que si ses dépendances changent. Voici pourquoi cela est important :

  • Optimisation des Performances : En mémorisant des valeurs coûteuses à calculer, vous évitez des recalculs inutiles lors des re-renders.
  • Rendu Plus Efficace : Les composants enfants qui utilisent cette valeur mémorisée ne seront pas forcés de se re-renderer si la valeur reste inchangée.

Contrairement à `useState`, `useMemo` n'induit pas de re-render lorsqu'il met à jour sa valeur. Il est donc idéal pour des cas où vous voulez simplement optimiser des calculs sans affecter l'état du composant.

2. Création d'une Valeur Mémorisée avec `useMemo`

Voici un exemple simple où nous utilisons `useMemo` pour mémoriser une valeur calculée basée sur un état local :

📋 Copier le code

import React, { useState, useMemo } from 'react';
import { createRoot } from 'react-dom/client';

function App() {
const [compte, setCompte] = useState(0); // État principal
const [nom, setNom] = useState('Alice'); // État secondaire

// Valeur mémorisée avec useMemo
const doubleCompte = useMemo(() => {
console.log('Calcul du double du compte...');
return compte * 2; // Calcule le double de "compte"
}, [compte]); // La valeur sera recalculée uniquement si "compte" change

function changerNom() {
setNom((prevState) => prevState === 'Alice' ? 'Bob' : 'Alice');
}

return (
<div>
	<h1>Exemple avec useMemo</h1>
	<p>Compteur : {compte}</p>
	<p>Nom : {nom}</p>
	<p>Double du compteur : {doubleCompte}</p>
	<button onClick={() => setCompte(compte + 1)}>Incrémenter</button>
	<button onClick={changerNom}>Changer de nom</button>
</div>
);
}

const root = createRoot(document.getElementById('root'));
root.render(<App />);

Explication détaillée du code React avec useMemo

Notre code React utilise useMemo pour optimiser le calcul du double de la valeur du compteur (compte).

1. État avec useState

  • compte : Stocke la valeur du compteur.
  • nom : Stocke un nom qui peut être changé entre "Alice" et "Bob".

2. Utilisation de useMemo

La fonction doubleCompte est mémorisée grâce à useMemo, ce qui signifie qu'elle ne sera recalculée que lorsque compte change.

Si nom est modifié, doubleCompte ne sera pas recalculé inutilement, ce qui optimise les performances.

3. Fonctionnement des boutons

  • Un bouton pour incrémenter la valeur du compteur.
  • Un autre bouton pour changer de nom.

4. Comportement attendu

  • Lorsque vous cliquez sur "Incrémenter", compte augmente de 1 et doubleCompte est recalculé.
  • Lorsque vous cliquez sur "Changer de nom", seul nom change, et doubleCompte reste inchangé.

5. Pourquoi utiliser useMemo ici ?

Dans une petite application comme celle-ci, useMemo n'apporte pas de grand bénéfice, mais dans des applications plus complexes où les calculs sont lourds, useMemo évite des recalculs inutiles et améliore les performances.

Explication détaillée ligne par ligne

1.  import React, { useState, useMemo } from 'react';
2.  import { createRoot } from 'react-dom/client';

// Importation de React et des hooks nécessaires : useState pour gérer 
//l'état et useMemo pour optimiser le calcul du double du compteur.
// createRoot est utilisé pour le rendu de l'application dans 
//l'élément HTML avec l'ID 'root'.

3.  function App() { 
4.      const [compte, setCompte] = useState(0); 
5.      const [nom, setNom] = useState('Alice');

// Déclaration du composant fonctionnel "App".
// Définition de l'état `compte` avec une valeur initiale de 0.
// Définition de l'état `nom` avec une valeur initiale "Alice".

6.      const doubleCompte = useMemo(() => {
7.          console.log('Calcul du double du compte...');
8.          return compte * 2;
9.      }, [compte]);

// Utilisation de useMemo pour mémoriser la valeur de `doubleCompte`.
// Cette fonction sera exécutée seulement si `compte` change.
// À chaque mise à jour de `compte`, un message "Calcul du double 
//du compte..." est affiché dans la console.

10.     function changerNom() { 
11.         setNom((prevState) => prevState === 'Alice' ? 'Bob' : 'Alice'); 
12.     }

// Définition de la fonction "changerNom" qui bascule entre 
//"Alice" et "Bob" à chaque appel.

13.  return (
14.   <div>
15.   <h1>Exemple avec useMemo</h1>
16.   <p>Compteur : {compte}</p>
17.   <p>Nom : {nom}</p>
18.   <p>Double du compteur : {doubleCompte}</p>
19.   <button onClick={() => setCompte(compte + 1)}
>Incrémenter</button>
20.   <button onClick={changerNom}>Changer de nom</button>
21.  </div>
22.     );

// Retour du JSX contenant l'affichage de l'application.
// Affichage du compteur, du nom et du double du compteur.
// Ajout de boutons pour incrémenter le compteur et changer de nom.

23. }

24. const root = createRoot(document.getElementById('root'));
25. root.render(<App />);

// Sélection de l'élément HTML avec l'ID 'root' et rendu du composant App.

6. Optimisation supplémentaire

Si vous souhaitez optimiser encore plus, vous pouvez utiliser useCallback pour éviter la recréation de fonctions à chaque rendu.

3. Exemple Avancé : Calcul Coûteux avec `useMemo`

Imaginons que vous ayez besoin de calculer une valeur coûteuse (par exemple, un tableau de nombres filtrés). Voici comment utiliser `useMemo` pour optimiser ce calcul :

📋 Copier le code

import React, { useState, useMemo } from 'react';
import { createRoot } from 'react-dom/client';

// Fonction coûteuse pour filtrer des nombres pairs
function filtreNombresPairs(nombres) {
console.log('Filtrage des nombres pairs...');
return nombres.filter((nombre) => nombre % 2 === 0);
}

function App() {
const [nombres, setNombres] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
const [filtreActif, setFiltreActif] = useState(false);

// Valeur mémorisée avec useMemo
const nombresFiltres = useMemo(() => {
if (!filtreActif) return nombres; // Retourne le tableau original si le filtre n'est pas activé
return filtreNombresPairs(nombres); // Sinon, applique le filtre coûteux
}, [filtreActif, nombres]); // Recalcule uniquement si "filtreActif" ou "nombres" change

function toggleFiltre() {
setFiltreActif((prevState) => !prevState);
}

return (
<div>
	<h1>Exemple avancé avec useMemo</h1>
	<button onClick={toggleFiltre}>Activer/Désactiver le filtre</button>
	<ul>
		{nombresFiltres.map((nombre) => (
			<li key={nombre}>{nombre}</li>
		))}
	</ul>
</div>
);
}

const root = createRoot(document.getElementById('root'));
root.render(<App />);

Explication détaillée du code React

Importation des modules nécessaires

On importe React et ses hooks useState et useMemo, ainsi que createRoot de react-dom/client pour le rendu.

Définition d'une fonction de filtrage

filtreNombresPairs() est une fonction qui filtre les nombres pairs d'un tableau donné. Elle affiche un message dans la console à chaque exécution.

Définition du composant App

Déclaration du composant fonctionnel App.

Initialisation des états

Déclaration de l’état nombres contenant un tableau de nombres de 1 à 10.

Déclaration de l’état filtreActif, initialisé à false, indiquant si le filtre des nombres pairs est activé.

Utilisation de useMemo

useMemo permet de mémoriser le résultat du filtrage. Il est recalculé uniquement si filtreActif ou nombres changent :

  • Si filtreActif est false, on retourne le tableau original.
  • Sinon, on applique filtreNombresPairs().

Gestion du bouton de filtrage

20-22 : toggleFiltre() est une fonction qui inverse l’état de filtreActif lorsqu'on clique sur le bouton.

Rendu du composant

24-30 : Le composant retourne une structure HTML contenant :

  • Un titre <h1> expliquant l’exemple.
  • Un bouton qui active/désactive le filtre.
  • Une liste affichant soit tous les nombres, soit seulement les nombres pairs selon l'état du filtre.

Montage du composant

32-34 : On récupère l’élément HTML avec document.getElementById('root'), puis on utilise createRoot pour y rendre le composant App.

4. Comparaison entre `useMemo` et d'autres Hooks

Bien que `useMemo` puisse sembler similaire à d'autres Hooks comme `useState` ou `useCallback`, il existe des différences clés qui définissent ses cas d'utilisation spécifiques. Voici une comparaison détaillée :

Hook Usage Re-renders Induits Cas d'Utilisation Typique
useState Gère l'état local d'un composant. Déclenche un re-render lorsqu'il est mis à jour. Gestion de valeurs d'état comme un compteur, un texte, etc.
useCallback Mémorise des fonctions pour éviter leur recréation lors des re-renders. N'émet pas de re-render lui-même, mais peut influencer des enfants optimisés avec React.memo. Optimisation des performances pour des fonctions passées via props à des enfants React.memo.
useMemo Mémorise des valeurs calculées pour éviter des recalculs inutiles. N'émet pas de re-render lui-même. Optimisation des performances pour des calculs coûteux ou des transformations de données complexes.

Comme vous pouvez le voir, useMemo est idéal lorsque vous avez besoin de mémoriser une valeur calculée sans affecter directement le cycle de vie du composant.

5. Meilleures Pratiques pour Utiliser `useMemo`

Pour utiliser useMemo efficacement, voici quelques conseils pratiques :

  • Évitez d'abuser de `useMemo` : Trop de mémorisation peut rendre votre code moins lisible et plus complexe. Utilisez-le uniquement pour des calculs coûteux ou répétitifs.
  • Choisissez soigneusement vos dépendances : Incluez uniquement les variables nécessaires dans le tableau de dépendances. Ajouter des dépendances inutiles entraînera des recalculs fréquents, annulant ainsi les avantages de performance.
  • Testez avant et après l'optimisation : Mesurez les performances de votre application avant et après avoir appliqué `useMemo`. Cela vous aidera à évaluer si cette optimisation est nécessaire.

6. Exemple Concret : Calcul de la Moyenne dans une Liste de Nombres

Imaginons que vous souhaitez afficher la moyenne d'une liste de nombres. Voici comment utiliser useMemo pour mémoriser cette valeur calculée et éviter des recalculs inutiles :

📋 Copier le code

import React, { useState, useMemo } from 'react';
import { createRoot } from 'react-dom/client';

function App() {
const [nombres, setNombres] = useState([1, 2, 3, 4, 5]);
const [filtreActif, setFiltreActif] = useState(false);

// Fonction coûteuse pour calculer la moyenne
function calculerMoyenne(nombres) {
console.log('Calcul de la moyenne...');
if (nombres.length === 0) return 0;
const somme = nombres.reduce((total, nombre) => total + nombre, 0);
return somme / nombres.length;
}

// Valeur mémorisée avec useMemo
const moyenne = useMemo(() => {
if (!filtreActif) return 0; // Si le filtre n'est pas activé, retourne 0
return calculerMoyenne(nombres); // Sinon, calcule la moyenne
}, [filtreActif, nombres]); // Recalcule uniquement si "filtreActif" ou "nombres" change

function toggleFiltre() {
setFiltreActif((prevState) => !prevState);
}

function ajouterNombre() {
setNombres((prevNombres) => [...prevNombres, Math.floor(Math.random() * 100)]); // Ajoute un nombre aléatoire à la liste
}

return (
<div>
	<h1>Exemple avancé avec useMemo : Calcul de la moyenne</h1>
	<button onClick={toggleFiltre}>Activer/Désactiver le calcul</button>
	<button onClick={ajouterNombre}>Ajouter un nombre aléatoire</button>
	<p>Filtre actif : {filtreActif ? 'Oui' : 'Non'}</p>
	<p>Moyenne : {moyenne.toFixed(2)}</p>
	<ul>
		{nombres.map((nombre, index) => (
			<li key={index}>{nombre}</li>
		))}
	</ul>
</div>
);
}

const root = createRoot(document.getElementById('root'));
root.render(<App />);

Explication détaillée ligne par ligne :

  1. function calculerMoyenne(nombres) { ... } :
    • Cette fonction simule un calcul coûteux en calculant la moyenne d'un tableau de nombres.
    • console.log('Calcul de la moyenne...'); : Affiche un message dans la console chaque fois que cette fonction est exécutée.
    • nombres.reduce((total, nombre) => total + nombre, 0) : Utilise la méthode reduce pour calculer la somme des nombres dans le tableau.
    • somme / nombres.length : Divise la somme par la longueur du tableau pour obtenir la moyenne.
  2. const moyenne = useMemo(() => { ... }, [filtreActif, nombres]); :
    • Cette ligne utilise useMemo pour mémoriser la valeur de la moyenne.
    • Le calcul ne sera effectué que si filteActif ou nombres change.
    • if (!filtreActif) return 0; : Si le filtre n'est pas activé, la moyenne est simplement définie à 0, évitant tout calcul coûteux.
    • return calculerMoyenne(nombres); : Si le filtre est activé, la fonction calculerMoyenne est appelée pour calculer la moyenne.
  3. function toggleFiltre() { ... } :
    • Cette fonction alterne l'état filteActif entre true et false.
    • Lorsque ce changement se produit, useMemo recalculera la moyenne uniquement si filteActif est true.
  4. function ajouterNombre() { ... } :
    • Cette fonction ajoute un nombre aléatoire au tableau nombres.
    • Math.floor(Math.random() * 100) : Génère un nombre entier aléatoire compris entre 0 et 99.
    • setNombres((prevNombres) => [...prevNombres, nouveauNombre]) : Met à jour l'état nombres en ajoutant le nouveau nombre généré.
  5. <button onClick={toggleFiltre}>Activer/Désactiver le calcul</button> :
    • Ce bouton appelle la fonction toggleFiltre pour activer/désactiver le calcul de la moyenne.
    • Lorsque le filtre est désactivé (false), aucun calcul n'est effectué grâce à useMemo.
  6. <button onClick={ajouterNombre}>Ajouter un nombre aléatoire</button> :
    • Ce bouton appelle la fonction ajouterNombre pour ajouter un nombre aléatoire au tableau nombres.
    • Cela déclenche également un recalcul de la moyenne, car nombres est une dépendance de useMemo.
  7. <p>Moyenne : {moyenne.toFixed(2)}</p> :
    • Cet élément affiche dynamiquement la moyenne des nombres dans le tableau.
    • moyenne.toFixed(2) : Formate la moyenne pour afficher deux chiffres après la virgule.
  8. <ul>{nombres.map((nombre, index) => (<li key={index}>{nombre}</li>))}</ul> :
    • Cette liste itère sur le tableau nombres pour afficher chaque nombre individuellement.
    • key={index} : Une clé unique est assignée à chaque élément pour aider React à identifier efficacement les changements dans la liste.

7. Quand Utiliser `useMemo` ?

Voici les scénarios où l'utilisation de useMemo est recommandée :

  • Calculs Coûteux : Lorsque vous avez besoin de calculer une valeur complexe ou coûteuse qui ne doit pas être recalculée à chaque re-render.
  • Transformations de Données : Par exemple, filtrer, trier ou transformer un grand tableau de données.
  • Optimisation des Performances : Si une valeur calculée est utilisée plusieurs fois dans un composant ou transmise à des enfants via props, useMemo peut réduire les recalculs inutiles.

Conclusion

Vous avez maintenant appris à utiliser le Hook useMemo pour mémoriser des valeurs calculées et optimiser les performances de vos applications React. En combinant useMemo avec des hooks comme useState et useCallback, vous pouvez créer des interfaces utilisateur réactives et performantes.

Prochain chapitre : Custom Hooks