Un bloc devenu perceptiblement lent dans l’éditeur : la réaction la plus fréquente consiste à parsemer le code de useMemo et useCallback, en espérant que cela règle le problème. Or ni l’un ni l’autre n’empêche, à lui seul, un composant de se re-rendre : ils évitent, sous certaines conditions précises, de refaire un calcul coûteux ou de recréer une fonction à chaque rendu. La nuance compte, car mal ciblée, cette optimisation alourdit le code sans gain mesurable.
Ce que useMemo mémorise réellement
useMemo conserve le résultat d’un calcul entre deux rendus, tant que les valeurs listées dans son tableau de dépendances n’ont pas changé. Il est utile lorsque ce calcul est réellement coûteux, un tri ou un filtrage sur une liste de plusieurs centaines d’éléments, par exemple, exécuté à chaque rendu du composant alors que les données sources changent rarement.
const articlesTries = useMemo( () => {
return [ ...articlesRecents ].sort( ( a, b ) =>
a.titre.localeCompare( b.titre )
);
}, [ articlesRecents ] );
Sur une liste de trois ou quatre éléments, ce même tri s’exécute en un temps négligeable, largement inférieur au coût du rendu React lui-même. Ajouter useMemo dans ce cas n’apporte rien de mesurable, tout en ajoutant une ligne de code et un tableau de dépendances de plus à maintenir correctement.
Ce que useCallback mémorise, et pourquoi cela compte moins souvent qu’on ne le pense
useCallback conserve la même référence de fonction entre deux rendus, tant que ses dépendances n’ont pas changé. Ce mécanisme n’a d’effet perceptible que si cette référence est ensuite comparée quelque part, typiquement lorsqu’elle est transmise en propriété à un composant enfant enveloppé dans memo(), qui compare justement ses propriétés par référence pour décider s’il doit se re-rendre.
const ComposantEnfant = memo( function ComposantEnfant( { onSelection } ) {
// se re-rendra uniquement si onSelection change de référence
return <button onClick={ onSelection }>Sélectionner</button>;
} );
const gererSelection = useCallback( () => {
setAttributes( { elementSelectionne: id } );
}, [ id ] );

Si le composant enfant n’est pas mémoïsé avec memo(), il se re-rendra de toute façon à chaque rendu du parent, que la fonction transmise change de référence ou non : useCallback devient alors inutile, puisque rien en aval n’exploite la stabilité de cette référence.
Mesurer avant d’optimiser
- Activer le profileur de React DevTools et enregistrer une séquence d’interactions représentative sur le bloc concerné.
- Repérer les composants qui se re-rendent le plus fréquemment et le temps réellement passé dans chacun.
- Identifier, parmi ces composants, ceux dont le rendu effectue un calcul coûteux ou transmettent des propriétés à des enfants mémoïsés.
- N’appliquer
useMemoouuseCallbackque sur ces points précis, puis rejouer la même séquence pour vérifier le gain réel.
Le cas où l’optimisation la plus efficace n’est ni l’un ni l’autre
Souvent, le vrai gain de performance sur un bloc complexe ne vient pas d’une mémoïsation de calcul ou de fonction, mais d’une meilleure structuration des sélecteurs useSelect, qui peuvent redéclencher un rendu à chaque changement d’un store entier si leur sélection n’est pas suffisamment précise. Réduire la portée de ces sélecteurs, avant même d’envisager useMemo ou useCallback, résout fréquemment le ralentissement observé sans ajouter la moindre ligne de mémoïsation.
- Vérifier d’abord la précision des sélecteurs
useSelectutilisés dans le composant. - Réserver
useMemoaux calculs dont le coût est mesurablement significatif. - Réserver
useCallbackaux fonctions transmises à des composants enfants réellement mémoïsés.
Une règle simple à garder en tête : une optimisation qu’on ne peut pas mesurer n’en est pas une, c’est une supposition qui alourdit le code en échange d’un gain hypothétique.
En résumé
useMemo et useCallback ne suppriment pas un rendu ; ils évitent, dans des conditions précises, un recalcul ou une recréation de fonction inutiles. Utilisés sans mesure préalable, ils ajoutent de la complexité sans garantie de gain. Profiler d’abord, cibler ensuite les points réellement coûteux, reste la seule méthode qui évite d’optimiser à l’aveugle un bloc devenu lent.