useCallback — concepts clés, le hook de mémoïsation de fonctions dans React

Auteur : IT Sectr Publié le : 2026-07-04 Temps de lecture : 9 min

useCallback est un hook React qui retourne une version mémoïsée d'une fonction qui ne change pas entre les rendus jusqu'à ce que ses dépendances changent. Contrairement à une déclaration de fonction normale dans un composant (qui crée une nouvelle fonction à chaque rendu), useCallback stabilise la référence de la fonction, évitant ainsi les re-rendus inutiles des composants enfants optimisés avec React.memo. Selon React Documentation (2025), useCallback n'est utile qu'en combinaison avec React.memo ou des hooks qui dépendent d'une référence stable.

Points clés

  • useCallback — un hook pour mémoïser une fonction, préservant la référence entre les rendus.
  • React.memo — useCallback n'est efficace qu'avec React.memo pour prévenir les re-rendus.
  • Dépendances — la fonction n'est recréée que lorsque les valeurs spécifiées dans le tableau changent.
  • Référence stable — useCallback garantit que la référence de la fonction ne changera pas inutilement.
  • Ne pas abuser — l'utilisation excessive de useCallback sans React.memo nuit aux performances.

Qu'est-ce que useCallback dans React

useCallback est un hook ajouté dans React 16.8 qui mémoïse une fonction : il retourne la même référence jusqu'à ce que les dépendances changent. Sans useCallback, chaque déclaration de fonction dans un composant crée un nouvel objet fonction à chaque rendu. Pour les primitifs, cela est imperceptible, mais en passant ces callbacks à des composants enfants optimisés avec React.memo, chaque nouvelle référence provoque un re-rendu du composant enfant.

Syntaxiquement, useCallback est équivalent à useMemo pour une fonction : useCallback(fn, deps) est un raccourci pour useMemo(() => fn, deps). React stocke la fonction mémoïsée dans le stockage interne du nœud fiber et compare les dépendances à chaque rendu. Si les dépendances n'ont pas changé (Object.is pour chaque élément), la fonction précédente est retournée.

Selon React Documentation — useCallback (2025), il ne faut pas envelopper chaque fonction dans useCallback. Le hook a son coût : appeler le hook, comparer les dépendances et allouer de la mémoire pour le tableau de dépendances. Si un composant est simple et n'a pas d'arbres profonds avec React.memo, useCallback ne fera que ralentir l'application. L'optimisation doit être mesurable, pas intuitive.

jsx
import { useCallback } from 'react';

function Parent() {
    const [count, setCount] = useState(0);

    // Référence stable — même fonction jusqu'à ce que les deps changent
    const handleClick = useCallback(() => {
        setCount(prev => prev + 1);
    }, []);

    return <Child onClick={handleClick} />;
}

Comment fonctionne la mémoïsation de fonctions

La mémoïsation dans useCallback est basée sur la mise en cache du résultat de l'appel de fonction. React préserve la fermeture (closure) créée lors du premier rendu et la retourne lors des rendus suivants tant que les dépendances restent inchangées. À l'intérieur du nœud fiber, chaque appel useCallback crée un nœud dans la liste chaînée des hooks, où les dépendances précédentes et la valeur mémoïsée sont stockées.

La comparaison des dépendances est effectuée strictement via Object.is — une comparaison superficielle sans vérification profonde des objets ou tableaux. Si une dépendance est un objet ou un tableau, une nouvelle référence à chaque rendu sera considérée comme un changement. Par conséquent, le tableau de dépendances doit contenir des valeurs primitives ou des références stables (par exemple, de useRef ou useMemo).

Selon React Core Team — Optimization Guide (2024), le coût de la mémoïsation comprend trois composants : allouer le tableau de dépendances à chaque rendu, parcourir et comparer les éléments via Object.is, et la surcharge potentielle du ramasse-miettes lors de la recréation. Pour un composant avec des centaines de wrappers useCallback, cela peut devenir perceptible — donc la sélectivité dans l'utilisation du hook est critique.

ScénarioSans useCallbackAvec useCallback
Création de fonctionNouvelle à chaque renduIdentique avec deps stables
Passé à React.memoL'enfant se re-rendL'enfant ne se re-rend pas
Dans le tableau de useEffectL'effet redémarreL'effet est stable
SurchargeMinimaleComparaison des dépendances + mémoire

useCallback et les performances

Il existe un mythe très répandu selon lequel useCallback améliore automatiquement les performances. En réalité, isolément (sans React.memo), useCallback ralentit même légèrement l'application en raison du coût de la comparaison des dépendances. Le hook n'apporte un réel bénéfice que dans trois scénarios : empêcher les re-rendus des composants React.memo, stabiliser les callbacks dans useEffect et passer des callbacks à des hooks personnalisés qui dépendent de l'égalité de référence.

La règle est simple : tant que vous n'avez pas détecté un problème de performance avec React DevTools Profiler — n'utilisez pas useCallback. L'équipe React a souligné à plusieurs reprises que l'optimisation prématurée est la racine de tous les maux. D'abord, écrivez du code propre sans mémoïsation, mesurez, trouvez le goulot d'étranglement dans le profileur, et alors seulement ajoutez useCallback là où c'est vraiment nécessaire.

jsx
// Optimisation mesurable : Child est enveloppé dans React.memo
const Child = React.memo(({ onClick }) => {
    console.log('Child re-rendu');
    return <button onClick={onClick}>Click</button>;
});

function Parent() {
    const handleClick = useCallback(() => {
        console.log('cliqué');
    }, []);
    return <Child onClick={handleClick} />;
}

Selon Dan Abramov — Before You memo() (2024), plus de 90% des cas d'utilisation de useCallback dans les projets open-source sont redondants. Les développeurs enveloppent chaque fonction « au cas où » sans mesurer l'effet. L'alternative : si le composant enfant est lourd et son re-rendu coûteux — React.memo + useCallback est justifié. Si le composant enfant est léger — le re-rendu est moins coûteux que la comparaison des dépendances.

Quand utiliser useCallback

Le premier scénario est React.memo. Si un composant enfant est enveloppé dans React.memo et reçoit une fonction de callback comme prop, sans useCallback, le composant enfant se re-rend à chaque rendu du parent, même si ses propres données n'ont pas changé. useCallback stabilise la référence, permettant à React.memo d'ignorer correctement le re-rendu.

Le deuxième scénario est useEffect avec un callback dans les dépendances. Si une fonction est passée dans le tableau de dépendances de useEffect, chaque nouvelle référence redémarre l'effet. useCallback garantit que la référence est stable et que l'effet ne s'exécute que lorsque les données réelles changent, pas à chaque rendu. Ceci est particulièrement important pour les abonnements et les requêtes.

  • Enfants React.memo — empêche le re-rendu des composants enfants mémoïsés lors du passage de callbacks.
  • Dépendances de useEffect — stabilise la fonction dans le tableau de dépendances de l'effet, évitant les redémarrages inutiles.
  • Hooks personnalisés — si un hook accepte un callback et dépend de son égalité de référence, useCallback est obligatoire.
  • Valeur de contexte — si une fonction est passée dans la valeur du contexte, useCallback stabilise la référence.
jsx
// useCallback pour dépendance stable de useEffect
const fetchData = useCallback(async (id) => {
    const res = await fetch(`/api/${id}`);
    setData(res.data);
}, []); // référence stable, jamais recréée

useEffect(() => {
    fetchData(props.id);
}, [props.id, fetchData]); // l'effet s'exécute seulement quand props.id change

useCallback vs useMemo

La principale différence entre useCallback et useMemo est ce que chacun mémoïse. useCallback mémoïse une fonction : useCallback(fn, deps) retourne fn (la même ou la version précédente). useMemo mémoïse le résultat d'un appel de fonction : useMemo(() => computeExpensive(a, b), [a, b]) retourne la valeur calculée, pas une fonction.

Techniquement, useCallback est du sucre syntaxique sur useMemo : useCallback(fn, deps) est équivalent à useMemo(() => fn, deps). Cette syntaxe n'existe que pour la lisibilité — pour que le développeur voie clairement qu'une fonction est mémoïsée, pas une valeur. Il n'y a pas de différence de performance entre useCallback et useMemo avec une fonction — ils génèrent un code identique.

HookMémoïseSyntaxeUtilisation
useCallbackFonction (référence)useCallback(fn, deps)Callbacks pour composants enfants
useMemoRésultat de calculuseMemo(() => value, deps)Calculs coûteux, mémoïsation d'objets
jsx
// Ceux-ci sont équivalents :
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);

Erreurs courantes avec useCallback

L'erreur la plus courante est d'envelopper inutilement toutes les fonctions dans useCallback sans React.memo sur les composants enfants. Si un composant enfant n'est pas enveloppé dans React.memo, il se re-rendra quand même à chaque rendu du parent, que la référence du callback change ou non. useCallback sans React.memo, c'est du coût sans bénéfice.

  • useCallback sans React.memo — le composant enfant se re-rend quand même ; l'optimisation est inutile.
  • Dépendances oubliées — si une variable utilisée dans le callback n'est pas dans deps, le callback contient une fermeture obsolète (stale closure).
  • Mémoïsation excessive — envelopper chaque fonction, y compris les onClick triviaux avec console.log.
  • Objets et tableaux dans les dépendances — une nouvelle référence à chaque rendu est considérée comme un changement, la mémoïsation est inutile.
jsx
// ❌ Inutile : pas de React.memo sur l'enfant
const handle = useCallback(() => doStuff(), []);
<Child onClick={handle} />; // Child se re-rend encore sans React.memo

// ❌ Stale closure : dépendance manquante
const handle = useCallback(() => {
    console.log(count); // count est toujours 0 — stale closure !
}, []);

// ✅ Correct : inclure les dépendances
const handle = useCallback(() => {
    console.log(count);
}, [count]);

Le problème de fermeture obsolète (stale closure) dans useCallback est résolu en incluant toutes les variables utilisées dans le tableau de dépendances. eslint-plugin-react-hooks avec exhaustive-deps vérifie automatiquement que toutes les variables du corps du callback sont présentes dans le tableau. Si le callback utilise setState, qui ne change pas entre les rendus, il peut être inclus en toute sécurité dans deps — React garantit la stabilité de setState.

Foire aux questions

Faut-il envelopper chaque fonction dans useCallback ?

Non. useCallback n'a de sens que dans trois cas : le composant enfant est enveloppé dans React.memo, la fonction est utilisée dans le tableau de dépendances de useEffect, ou la fonction est passée à un hook personnalisé qui dépend de l'égalité de référence. Dans les autres cas, useCallback ajoute une surcharge sans bénéfice. L'équipe React recommande d'écrire d'abord sans optimisations et de les ajouter en fonction des résultats du profilage.

Qu'est-ce qui est le plus rapide — useCallback ou une nouvelle fonction à chaque rendu ?

Pour les composants simples — une nouvelle fonction à chaque rendu est légèrement plus rapide, car useCallback dépense des ressources pour comparer les dépendances et allouer le tableau. Pour les composants avec des arbres React.memo profonds, useCallback gagne en empêchant le re-rendu de milliers d'éléments enfants. Mesurez et comparez au lieu de deviner — utilisez React DevTools Profiler pour une évaluation objective.

Peut-on utiliser useCallback avec des fonctions async ?

Oui, useCallback fonctionne avec les fonctions async exactement de la même manière qu'avec les fonctions synchrones. Le hook mémoïse la fonction elle-même, et le résultat (une Promesse) est retourné à chaque appel. Une fonction async dans useCallback est un modèle courant pour les callbacks stables de chargement de données utilisés dans useEffect : const fetchData = useCallback(async (id) => {...}, []).

Comment déboguer les problèmes avec useCallback et React.memo ?

Utilisez React DevTools Profiler — il montre quels composants se re-rendent et pourquoi. Pour une vérification programmatique, ajoutez console.log ou utilisez useWhyDidYouUpdate — une bibliothèque qui consigne la raison du re-rendu. Les principales raisons : une prop a changé (y compris la référence du callback), l'état a changé ou le contexte a changé. Si useCallback n'aide pas — vérifiez que toutes les dépendances sont correctement spécifiées.

Comment passer useCallback à un composant enfant sans React.memo ?

Même sans React.memo, useCallback peut être utile en combinaison avec useMemo pour les valeurs de contexte. Si vous passez un objet avec des fonctions à Context.Provider, enveloppez la création de l'objet dans useMemo et chaque fonction dans useCallback. Cela évite le re-rendu de tous les consommateurs du contexte lorsqu'une des fonctions change. Mais pour passer directement des callbacks dans des props sans React.memo, useCallback n'apporte aucun bénéfice.

Résumé

  • useCallback — un hook pour mémoïser une fonction, retournant une référence stable jusqu'à ce que les dépendances changent.
  • Synergie avec React.memo — useCallback n'est efficace qu'avec React.memo sur les composants enfants.
  • Comparaison des dépendances — via Object.is ; les objets/tableaux dans deps cassent la mémoïsation.
  • Mesurez, ne devinez pas — ajoutez useCallback seulement après avoir détecté un goulot d'étranglement dans le profileur.
  • Fermeture stable — incluez toutes les variables utilisées dans le tableau de dépendances, sinon — fermeture obsolète.
  • useCallback vs useMemo — useCallback mémoïse une fonction, useMemo mémoïse le résultat d'un calcul.
  • Excès — plus de 90% des utilisations de useCallback dans les projets réels sont une optimisation prématurée.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi