Optimiser React : useMemo, memo ou useCallback

Dans une application React, chaque rendu supplémentaire n’est pas forcément un problème. Le véritable enjeu consiste à limiter les calculs coûteux, les rendus en cascade et la recréation d’objets ou de fonctions lorsque ces opérations ont un impact mesurable sur l’interface.

useMemo, memo et useCallback répondent à des besoins différents. Le premier mémorise une valeur calculée, le deuxième évite certains rendus d’un composant, tandis que le troisième conserve la même référence de fonction entre deux rendus.

Une optimisation efficace commence donc par l’observation. Le profileur React, les outils de développement du navigateur et une analyse des interactions permettent de distinguer un ralentissement réel d’une simple préférence stylistique.

Comprendre Le Coût D’un Rendu

Lorsqu’un composant parent est rendu, React peut évaluer à nouveau ses enfants. Cette étape ne signifie pas nécessairement que le DOM sera entièrement modifié : React compare l’arbre virtuel et applique uniquement les changements nécessaires. Cependant, les calculs JavaScript effectués pendant le rendu peuvent devenir coûteux avec de grandes listes, des filtres complexes ou des transformations de données répétées.

La comparaison des propriétés repose souvent sur l’égalité référentielle. Deux objets ayant le même contenu, mais créés à deux moments différents, sont considérés comme différents. Il en va de même pour deux fonctions recréées à chaque exécution du composant. Cette distinction explique pourquoi memo seul ne suffit pas toujours.

Avant d’ajouter une mémorisation, mesurez le comportement observé dans une configuration représentative. Une analyse de performance peut aussi aider à repérer les rendus inutiles, les calculs dominants et les interactions qui dégradent la fluidité.

Utiliser Memo Pour Stabiliser Un Composant

memo est une fonction d’ordre supérieur qui permet à un composant d’ignorer un rendu lorsque ses propriétés n’ont pas changé selon une comparaison superficielle. Elle est particulièrement utile pour un composant enfant relativement coûteux, affiché souvent, mais recevant des propriétés stables.

const LigneProduit = memo(function LigneProduit({ produit, selectionne }) {
  return (
    <li className={selectionne ? "active" : ""}>
      {produit.nom}
    </li>
  );
});

Cette technique perd une grande partie de son intérêt si le parent transmet systématiquement des objets littéraux, des tableaux nouvellement créés ou des fonctions anonymes. Dans ce cas, memo détecte une nouvelle référence à chaque rendu et laisse le composant s’exécuter normalement.

Une fonction de comparaison personnalisée peut traiter certains cas, mais elle ajoute son propre coût et sa propre complexité. Elle doit rester exceptionnelle, car une comparaison profonde mal conçue peut coûter plus cher que le rendu évité.

Réserver UseMemo Aux Calculs Coûteux

useMemo conserve le résultat d’une expression entre deux rendus, tant que les dépendances restent identiques. Il convient aux opérations dont le calcul est réellement significatif : tri d’un catalogue volumineux, filtrage d’une collection importante ou construction d’une structure de données complexe.

const produitsVisibles = useMemo(() => {
  return produits
    .filter((produit) => produit.nom.includes(recherche))
    .sort((a, b) => a.prix - b.prix);
}, [produits, recherche]);

Il ne faut pas transformer chaque expression en valeur mémorisée. Une addition simple, une propriété lue sur un objet ou un petit tableau ne justifient généralement pas le coût mental et technique de cette optimisation. Les dépendances doivent aussi être complètes : les omettre peut produire une valeur obsolète difficile à diagnostiquer.

useMemo stabilise parfois une référence transmise à un composant enveloppé par memo. Son rôle principal reste toutefois la mémorisation d’une valeur, et non la prévention directe du rendu d’un composant.

Comparer Les Trois Mécanismes

Les trois outils interviennent à des niveaux différents. memo agit sur le composant, useMemo sur une valeur calculée et useCallback sur une fonction. Les associer peut être pertinent, mais seulement lorsque leurs conditions d’efficacité sont réunies.

Mécanisme Élément mémorisé Cas d’usage Risque fréquent
memo Résultat du rendu d’un composant Enfant coûteux avec propriétés stables Croire qu’il bloque tous les rendus
useMemo Valeur calculée Filtre, tri ou transformation lourde Dépendances incomplètes ou calcul insignifiant
useCallback Référence d’une fonction Fonction passée à un enfant mémorisé Mémoriser des fonctions sans bénéfice

useCallback est donc surtout utile avec memo ou lorsqu’une fonction figure dans les dépendances d’un autre Hook. Sans composant sensible à l’identité de la fonction, conserver cette référence ne procure souvent aucun gain visible.

Le coût de ces Hooks reste généralement faible, mais il n’est pas nul. Ils rendent le code plus dense, compliquent parfois le débogage et peuvent masquer un problème d’architecture, comme un état placé trop haut dans l’arbre ou un composant trop chargé.

Choisir La Bonne Stratégie

Employez memo lorsque le composant enfant reçoit des propriétés primitives ou des références stabilisées et que son rendu est suffisamment coûteux pour justifier une comparaison. Une ligne d’interface très simple ne bénéficiera presque jamais d’une telle protection.

Préférez useMemo lorsqu’une valeur dérivée demande un calcul mesurable. Pour stabiliser un objet transmis à un enfant, il peut aussi être pertinent, mais cette motivation doit rester secondaire. Si la valeur peut être calculée directement sans impact perceptible, la version la plus simple est souvent préférable.

Utilisez useCallback lorsqu’une fonction est transmise à un composant mémorisé, utilisée dans un tableau de dépendances ou consommée par un Hook qui dépend de son identité. Il ne sert pas à rendre une fonction plus rapide : il évite surtout de créer une nouvelle référence.

Repérer Les Optimisations Utiles

Le profileur React permet de comparer la durée des rendus avant et après une modification. Examinez les interactions concrètes : saisie dans un champ, navigation entre des vues, ouverture d’une fenêtre ou mise à jour d’une liste.

Signaux justifiant une investigation :

Erreurs courantes à éviter :

Les problèmes de performance peuvent aussi venir du réseau ou des effets secondaires. Une requête répétée, un rafraîchissement mal contrôlé ou une relance immédiate après erreur doit être traité à son niveau. Dans ce contexte, une file de réessai avec temporisation progressive relève de la robustesse applicative, pas de la mémorisation React.

Construire Une Optimisation Durable

Commencez par une structure de composants claire. Descendre l’état au plus près de son usage réduit souvent les rendus descendants sans ajouter un seul Hook. La pagination, la virtualisation des longues listes et le découpage des composants peuvent produire un gain supérieur à une collection de mémorisations.

Les versions récentes de React et les outils associés peuvent automatiser certaines optimisations dans des conditions précises. Cela ne dispense pas de comprendre les références, les dépendances et le cycle de rendu : ces notions restent indispensables pour analyser un comportement inattendu et choisir une architecture adaptée.

Documentez les optimisations qui répondent à un problème mesuré, puis vérifiez leur effet dans la durée. Pour signaler un exemple à corriger ou proposer un sujet technique, utilisez le contact de la rédaction. 马会