useCallback — kernbegrippen, de hook voor het memoizen van functies in React

Auteur: IT Sectr Gepubliceerd: 2026-07-04 Leestijd: 9 min

useCallback — is een React-hook die een gememoïzeerde versie van de functie retourneert, die niet verandert tussen renders zolang de afhankelijkheden niet veranderen. In tegenstelling tot de gebruikelijke declaratie van een functie binnen de component (die bij elke render een nieuwe functie maakt), stabiliseert useCallback de referentie naar de functie, waardoor onnodige re-renders van via React.memo geoptimaliseerde kindcomponenten worden voorkomen. Volgens React Documentation (2025) is useCallback alleen nuttig in combinatie met React.memo of hooks die afhankelijk zijn van een stabiele referentie.

Belangrijkste punten

  • useCallback — hook voor het memoizen van functies, die de referentie tussen renders behoudt.
  • React.memo — useCallback is alleen effectief in combinatie met React.memo om re-renders te voorkomen.
  • Afhankelijkheden — de functie wordt alleen opnieuw aangemaakt bij wijziging van de opgegeven waarden in de array.
  • Stabiele referentie — useCallback garandeert dat de referentie naar de functie niet onnodig verandert.
  • Overdrijf niet — overmatig useCallback zonder React.memo verslechtert de prestaties.

Wat is useCallback in React

useCallback — is een hook die in React 16.8 is toegevoegd en de functie memoïzeert: het retourneert dezelfde referentie zolang de afhankelijkheden niet veranderen. Zonder useCallback creëert elke functiedeclaratie binnen de component bij elke render een nieuw functieobject. Voor primitieven is dit onmerkbaar, maar bij het doorgeven van dergelijke callbacks aan via React.memo geoptimaliseerde kindcomponenten veroorzaakt elke nieuwe referentie een re-render van de kindcomponent.

Syntactisch is useCallback gelijkwaardig aan useMemo voor functies: useCallback(fn, deps) — is een afkorting voor useMemo(() => fn, deps). React slaat de gememoïzeerde functie op in de interne opslag van het fiber-knooppunt en vergelijkt de afhankelijkheden bij elke render. Als de afhankelijkheden niet zijn veranderd (Object.is voor elk element), wordt de vorige functie geretourneerd.

Volgens React Documentation — useCallback (2025) hoef je niet elke functie in useCallback te wikkelen. De hook heeft zijn eigen prijs: het aanroepen van de hook, het vergelijken van afhankelijkheden en geheugentoewijzing voor de afhankelijkheidsarray. Als de component eenvoudig is en geen diepe bomen met React.memo heeft, zal useCallback de applicatie alleen maar vertragen. Optimalisatie moet meetbaar zijn, niet intuïtief.

jsx
import { useCallback } from 'react';

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

    // Stabiele referentie — dezelfde functie totdat de deps veranderen
    const handleClick = useCallback(() => {
        setCount(prev => prev + 1);
    }, []);

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

Hoe werkt functie-memoization

Memoization in useCallback is gebaseerd op het cachen van het resultaat van de functieaanroep. React bewaart de closure die op het moment van de eerste render is gemaakt en retourneert deze bij volgende renders zolang de afhankelijkheden ongewijzigd blijven. Binnen het fiber-knooppunt creëert elke useCallback-aanroep een knooppunt in de gelinkte lijst van hooks, waar de vorige afhankelijkheden en de gememoïzeerde waarde worden opgeslagen.

Het proces van het vergelijken van afhankelijkheden gebeurt strikt via Object.is — dit is een oppervlakkige vergelijking, zonder diepgaande controle van objecten of arrays. Als er een object of array in de afhankelijkheden zit, wordt een nieuwe referentie bij elke render als een wijziging beschouwd. Daarom moeten in de afhankelijkheidsarray primitieve waarden of stabiele referenties (bijvoorbeeld uit useRef of useMemo) worden doorgegeven.

Volgens React Core Team — Optimization Guide (2024) omvat de kosten van memoization drie componenten: het toewijzen van de afhankelijkheidsarray bij elke render, het doorlopen en vergelijken van elementen via Object.is en mogelijke overhead van garbage collection bij het opnieuw creëren. Voor een component met honderden useCallback-wrappers kan dit merkbaar worden — daarom is selectiviteit in het gebruik van de hook cruciaal.

ScenarioZonder useCallbackMet useCallback
Functie aanmakenNieuw bij elke renderZelfde bij stabiele deps
Doorgeven aan React.memoKind wordt re-renderedKind wordt niet re-rendered
In useEffect-arrayEffect wordt opnieuw gestartEffect is stabiel
OverheadMinimaalAfhankelijkheden vergelijken + geheugen

useCallback en prestaties

Er is een wijdverbreide mythe dat useCallback automatisch de prestaties verbetert. In werkelijkheid vertraagt useCallback in isolatie (zonder React.memo) de applicatie zelfs enigszins vanwege de kosten van het vergelijken van afhankelijkheden. De hook levert alleen in drie scenario's echt voordeel op: het voorkomen van re-renders van React.memo-componenten, de stabiliteit van callbacks in useEffect en het doorgeven aan custom hooks die afhankelijk zijn van reference equality.

De regel is eenvoudig: zolang je geen prestatieprobleem hebt gevonden via React DevTools Profiler, gebruik je useCallback niet. Het React-team heeft herhaaldelijk benadrukt dat voortijdige optimalisatie de wortel van alle kwaad is. Schrijf eerst schone code zonder memoization, meet, vind de bottleneck in de profiler en voeg pas daarna useCallback toe waar het echt nodig is.

jsx
// Meetbare optimalisatie: Child is in React.memo gewikkeld
const Child = React.memo(({ onClick }) => {
    console.log('Child is re-rendered');
    return <button onClick={onClick}>Click</button>;
});

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

Volgens Dan Abramov — Before You memo() (2024) is meer dan 90% van de useCallback-gebruiken in open-sourceprojecten overbodig. Ontwikkelaars wikkelen elke functie „voor het geval dat", zonder het effect te meten. Alternatief: als de kindcomponent zwaar is en de re-render duur — dan zijn React.memo + useCallback gerechtvaardigd. Als de kindcomponent licht is — is een re-render goedkoper dan het vergelijken van afhankelijkheden.

Wanneer useCallback gebruiken

Het eerste scenario — React.memo. Als de kindcomponent in React.memo is gewikkeld en een callback-functie als prop accepteert, wordt de kindcomponent zonder useCallback bij elke render van de ouder opnieuw getekend, zelfs als de eigen gegevens niet zijn veranderd. useCallback stabiliseert de referentie en React.memo kan de re-render correct overslaan.

Het tweede scenario — useEffect met callback in de afhankelijkheden. Als de functie wordt doorgegeven aan de afhankelijkheidsarray van useEffect, zal elke nieuwe referentie het effect opnieuw starten. useCallback garandeert dat de referentie stabiel is en het effect alleen wordt uitgevoerd bij wijziging van de echte gegevens, niet bij elke render. Dit is vooral belangrijk voor abonnementen en verzoeken.

  • React.memo children — voorkomt re-renders van gememoïzeerde kindcomponenten bij het doorgeven van callbacks.
  • useEffect dependencies — stabiliseert de functie in de afhankelijkheidsarray van het effect en voorkomt onnodige herstarts.
  • Custom hooks — als een hook een callback accepteert en afhankelijk is van de reference equality ervan, is useCallback verplicht.
  • Context value — als er een functie in de value van de context wordt doorgegeven, stabiliseert useMemo met useCallback de referentie.
jsx
// useCallback voor een stabiele useEffect-afhankelijkheid
const fetchData = useCallback(async (id) => {
    const res = await fetch(`/api/${id}`);
    setData(res.data);
}, []); // stabiele referentie, wordt nooit opnieuw aangemaakt

useEffect(() => {
    fetchData(props.id);
}, [props.id, fetchData]); // effect draait alleen als props.id verandert

useCallback vs useMemo

Het belangrijkste verschil tussen useCallback en useMemo is wat elk memoïzeert. useCallback memoïzeert de functie: useCallback(fn, deps) retourneert fn (dezelfde of de vorige versie). useMemo memoïzeert het resultaat van de functieaanroep: useMemo(() => computeExpensive(a, b), [a, b]) retourneert de berekende waarde, niet de functie.

Technisch gezien is useCallback syntactische suiker bovenop useMemo: useCallback(fn, deps) is gelijkwaardig aan useMemo(() => fn, deps). Deze syntaxis bestaat alleen voor leesbaarheid — zodat de ontwikkelaar duidelijk ziet dat er precies de functie wordt gememoïzeerd, niet de waarde. Er is geen verschil in prestaties tussen useCallback en useMemo met een functie — ze genereren dezelfde code.

HookMemoïzeertSyntaxisGebruik
useCallbackDe functie (referentie)useCallback(fn, deps)Callbacks voor kindcomponenten
useMemoHet rekenresultaatuseMemo(() => value, deps)Dure berekeningen, memoizen van objecten
jsx
// Deze zijn gelijkwaardig:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);

Veelvoorkomende fouten met useCallback

De meest voorkomende fout — het zinloos inpakken van alle functies in useCallback zonder React.memo op kindcomponenten. Als de kindcomponent niet in React.memo is gewikkeld, wordt deze toch bij elke render van de ouder re-rendered, ongeacht of de referentie van de callback verandert. useCallback zonder React.memo — kosten zonder voordeel.

  • useCallback zonder React.memo — de kindcomponent wordt toch re-rendered; de optimalisatie is zinloos.
  • Vergeten afhankelijkheden — als binnen de callback een variabele wordt gebruikt die niet in de deps staat, bevat de callback een stale closure.
  • Overmatige memoization — elke functie inpakken, inclusief triviale onClick met console.log.
  • Objecten en arrays in afhankelijkheden — een nieuwe referentie bij elke render wordt als een wijziging beschouwd, memoization is nutteloos.
jsx
// ❌ Nutteloos: geen React.memo op child
const handle = useCallback(() => doStuff(), []);
<Child onClick={handle} />; // Child wordt zonder React.memo toch re-rendered

// ❌ Stale closure: ontbrekende afhankelijkheid
const handle = useCallback(() => {
    console.log(count); // count is altijd 0 — stale closure!
}, []);

// ✅ Correct: neem afhankelijkheden op
const handle = useCallback(() => {
    console.log(count);
}, [count]);

Het stale closure-probleem in useCallback wordt opgelost door alle gebruikte variabelen in de afhankelijkheidsarray op te nemen. eslint-plugin-react-hooks met exhaustive-deps controleert automatisch of alle variabelen uit de callback-body in de array aanwezig zijn. Als de callback setState gebruikt, die niet verandert tussen renders, kan deze veilig in de deps worden vermeld — React garandeert de stabiliteit van setState.

Veelgestelde vragen

Moet ik elke functie in useCallback wikkelen?

Nee. useCallback heeft alleen zin in drie gevallen: de kindcomponent is in React.memo gewikkeld, de functie wordt gebruikt in de afhankelijkheidsarray van useEffect, of de functie wordt doorgegeven aan een custom hook die afhankelijk is van reference equality. In andere gevallen voegt useCallback overhead toe zonder voordeel. Het React-team raadt aan eerst zonder optimalisaties te schrijven en ze toe te voegen op basis van profilering.

Wat is sneller — useCallback of een nieuwe functie bij elke render?

Voor eenvoudige componenten is een nieuwe functie bij elke render iets sneller, omdat useCallback middelen verbruikt voor het vergelijken van afhankelijkheden en het toewijzen van arrays. Voor componenten met diepe React.memo-bomen wint useCallback, door de re-render van duizenden kindelementen te voorkomen. Meet en vergelijk, gok niet — gebruik React DevTools Profiler voor een objectieve beoordeling.

Kan ik useCallback met async-functies gebruiken?

Ja, useCallback werkt met async-functies precies hetzelfde als met synchrone. De hook memoïzeert de functie zelf, en het resultaat (Promise) wordt elke keer bij de aanroep geretourneerd. Een asynchrone functie binnen useCallback is een veelvoorkomend patroon voor stabiele data-loading callbacks die in useEffect worden gebruikt: const fetchData = useCallback(async (id) => {...}, []).

Hoe debug ik het probleem met useCallback en React.memo?

Gebruik React DevTools Profiler — het toont welke componenten worden re-rendered en waarom. Voor programmatische controle voeg je console.log of useWhyDidYouUpdate toe — een bibliotheek die de reden van de re-render logt. De belangrijkste oorzaken: er is een prop veranderd (inclusief de callback-referentie), er is state of context veranderd. Als useCallback niet helpt, controleer dan of alle afhankelijkheden correct zijn opgegeven.

Hoe geef ik useCallback door aan een kindcomponent zonder React.memo?

Zelfs zonder React.memo kan useCallback nuttig zijn in combinatie met useMemo voor de contextvalue. Als je een object met functies doorgeeft aan Context.Provider, wikkel het maken van het object dan in useMemo en elke functie in useCallback. Dit voorkomt de re-render van alle context-consumers bij het wijzigen van een van de functies. Maar voor het direct doorgeven van callbacks in props zonder React.memo biedt useCallback geen voordeel.

Conclusies

  • useCallback — hook voor het memoizen van functies, die een stabiele referentie retourneert tot de afhankelijkheden veranderen.
  • React.memo synergy — useCallback is alleen effectief in combinatie met React.memo op kindcomponenten.
  • Afhankelijkheden vergelijken — via Object.is; objecten/arrays in deps verbreken memoization.
  • Meet, gok niet — voeg useCallback alleen toe nadat je via de profiler een bottleneck hebt gevonden.
  • Stable closure — neem alle gebruikte variabelen op in de afhankelijkheidsarray, anders — stale closure.
  • useCallback vs useMemo — useCallback memoïzeert de functie, useMemo — het rekenresultaat.
  • Overbodigheid — meer dan 90% van de useCallback-gebruiken in echte projecten is voortijdige optimalisatie.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook