Kodokon kodokon.com

Mémoïsation : memo, useMemo, useCallback

Sachez quand React.memo, useMemo et useCallback accélèrent vraiment votre application, et quand ils la compliquent pour rien.

10 min · 3 questions

Ouvrir cette leçon dans Kodokon

Quand un composant re-rend, tous ses enfants re-rendent par défaut, que leurs props aient changé ou non. Ce n'est pas un drame : un rendu ne touche au DOM que si la sortie change. La mémoïsation devient pertinente quand des rendus fréquents rencontrent des sous-arbres coûteux. Trois outils, trois cibles : React.memo saute le rendu d'un composant si ses props sont superficiellement égales, useMemo met en cache une valeur calculée, useCallback met en cache une fonction. Les trois reposent sur l'égalité de références.

JSX
import { memo } from "react";

const Row = memo(function Row({ item, onSelect }) {
  return (
    <li onClick={() => onSelect(item.id)}>{item.label}</li>
  );
});
Row ne re-rend que si item ou onSelect change de référence.

La chaîne ne vaut que par son maillon le plus faible. Si une seule prop reçoit une référence neuve à chaque rendu - fonction fléchée inline, objet ou tableau littéral -, la comparaison de memo échoue et le composant re-rend quand même. useCallback stabilise la référence de la fonction pour que la comparaison réussisse.

JSX
import { useCallback } from "react";

function List({ items }) {
  const handleSelect = useCallback((id) => {
    console.log("selected:", id);
  }, []);

  return (
    <ul>
      {items.map((item) => (
        <Row
          key={item.id}
          item={item}
          onSelect={handleSelect}
        />
      ))}
    </ul>
  );
}
handleSelect garde la même référence à chaque rendu.

useMemo a deux usages légitimes : éviter un calcul réellement coûteux (tri ou agrégation sur des milliers d'éléments), et stabiliser une référence consommée par un enfant mémoïsé ou listée dans les dépendances d'un effet. Pour un calcul trivial, le cache coûte plus cher qu'il ne rapporte : comparaison des dépendances, mémoire retenue, lecture alourdie.

JSX
import { useMemo } from "react";

function Stats({ orders }) {
  const total = useMemo(() => {
    return orders.reduce(
      (sum, order) => sum + order.amount,
      0
    );
  }, [orders]);

  return <p>Total : {total} €</p>;
}
Le total n'est recalculé que si orders change de référence.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Un enfant est enveloppé dans React.memo, mais son parent lui passe une fonction fléchée inline en prop. Résultat ?
    • La mémoïsation fonctionne : les fonctions sont comparées par leur code source
    • La mémoïsation est inopérante : la fonction inline crée une référence neuve à chaque rendu et la comparaison échoue
    • React lève un avertissement en développement
  2. Que garantit useCallback(fn, [a]) tant que a ne change pas ?
    • Que la fonction n'est jamais recréée en mémoire
    • Que la fonction ne sera exécutée qu'une seule fois
    • Que la même référence de fonction est retournée d'un rendu à l'autre
  3. Dans quel cas useMemo est-il clairement justifié ?
    • Un calcul coûteux, ou une référence à stabiliser pour un enfant mémoïsé ou une dépendance d'effet
    • Toute valeur dérivée, par principe de précaution
    • La mise en cache de données entre deux montages du composant