useCallback — Schlüsselkonzepte, der Hook zur Funktionsmemoization in React

Autor: IT Sectr Veröffentlicht: 2026-07-04 Lesezeit: 9 Min.

useCallback ist ein React-Hook, der eine memoized Version einer Funktion zurückgibt, die sich zwischen Renderings nicht ändert, bis sich ihre Abhängigkeiten ändern. Im Gegensatz zu einer normalen Funktionsdeklaration innerhalb einer Komponente (die bei jedem Rendering eine neue Funktion erzeugt), stabilisiert useCallback die Referenz auf die Funktion, wodurch unnötige Neurenderungen von mit React.memo optimierten Kindkomponenten verhindert werden. Laut React Documentation (2025) ist useCallback nur in Kombination mit React.memo oder Hooks nützlich, die von einer stabilen Referenz abhängen.

Wichtige Punkte

  • useCallback — ein Hook zum Memoieren einer Funktion, der die Referenz zwischen Renderings bewahrt.
  • React.memo — useCallback ist nur mit React.memo wirksam, um Neurenderungen zu verhindern.
  • Abhängigkeiten — die Funktion wird nur neu erstellt, wenn sich die angegebenen Werte im Array ändern.
  • Stabile Referenz — useCallback garantiert, dass sich die Funktionsreferenz nicht unnötig ändert.
  • Nicht übermäßig verwenden — übermäßiger useCallback ohne React.memo verschlechtert die Leistung.

Was ist useCallback in React

useCallback ist ein in React 16.8 eingeführter Hook, der eine Funktion memoized: er gibt dieselbe Referenz zurück, bis sich die Abhängigkeiten ändern. Ohne useCallback erzeugt jede Funktionsdeklaration innerhalb einer Komponente bei jedem Rendering ein neues Funktionsobjekt. Bei Primitiven ist dies unbemerkt, aber beim Übergeben solcher Callbacks an mit React.memo optimierte Kindkomponenten verursacht jede neue Referenz eine Neurenderung der Kindkomponente.

Syntaxmäßig ist useCallback äquivalent zu useMemo für eine Funktion: useCallback(fn, deps) ist eine Kurzform von useMemo(() => fn, deps). React speichert die memoized Funktion im internen Speicher des Fiber-Knotens und vergleicht die Abhängigkeiten bei jedem Rendering. Wenn sich die Abhängigkeiten nicht geändert haben (Object.is für jedes Element), wird die vorherige Funktion zurückgegeben.

Laut React Documentation — useCallback (2025) sollte man nicht jede Funktion in useCallback einwickeln. Der Hook hat seinen Preis: Aufrufen des Hooks, Vergleichen der Abhängigkeiten und Speicherallokation für das Abhängigkeiten-Array. Wenn eine Komponente einfach ist und keine tiefen Bäume mit React.memo hat, wird useCallback die Anwendung nur verlangsamen. Optimierung sollte messbar sein, nicht intuitiv.

jsx
import { useCallback } from 'react';

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

    // Stabile Referenz — gleiche Funktion, bis sich deps ändern
    const handleClick = useCallback(() => {
        setCount(prev => prev + 1);
    }, []);

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

Wie Funktionsmemoization funktioniert

Die Memoization in useCallback basiert auf dem Caching des Ergebnisses des Funktionsaufrufs. React bewahrt den beim ersten Rendering erstellten Closure auf und gibt ihn bei nachfolgenden Renderings zurück, solange die Abhängigkeiten unverändert bleiben. Innerhalb des Fiber-Knotens erzeugt jeder useCallback-Aufruf einen Knoten in der verknüpften Liste der Hooks, in dem die vorherigen Abhängigkeiten und der memoized Wert gespeichert werden.

Der Abhängigkeitsvergleich wird strikt über Object.is durchgeführt — ein oberflächlicher Vergleich ohne tiefe Prüfung von Objekten oder Arrays. Wenn eine Abhängigkeit ein Objekt oder Array ist, wird eine neue Referenz bei jedem Rendering als Änderung betrachtet. Daher sollte das Abhängigkeiten-Array primitive Werte oder stabile Referenzen (z.B. von useRef oder useMemo) enthalten.

Laut React Core Team — Optimization Guide (2024) umfasst die Kosten der Memoization drei Komponenten: Allokation des Abhängigkeiten-Arrays bei jedem Rendering, Durchlaufen und Vergleichen der Elemente über Object.is und potenzieller Garbage-Collection-Overhead beim Neuerstellen. Für eine Komponente mit Hunderten von useCallback-Wrappern kann dies merkbar werden — daher ist Selektivität bei der Verwendung des Hooks entscheidend.

SzenarioOhne useCallbackMit useCallback
FunktionserstellungNeu bei jedem RenderingGleich bei stabilen deps
An React.memo übergebenKind rendert neuKind rendert nicht neu
Im useEffect-ArrayEffekt startet neuEffekt bleibt stabil
OverheadMinimalAbhängigkeitsvergleich + Speicher

useCallback und Leistung

Es gibt einen weit verbreiteten Mythos, dass useCallback automatisch die Leistung verbessert. In Wirklichkeit verlangsamt useCallback in Isolation (ohne React.memo) die Anwendung sogar geringfügig aufgrund der Kosten des Abhängigkeitsvergleichs. Der Hook bringt nur in drei Szenarien echten Nutzen: Verhindern von Neurenderungen von React.memo-Komponenten, Stabilisieren von Callbacks in useEffect und Übergeben von Callbacks an benutzerdefinierte Hooks, die von Referenzgleichheit abhängen.

Die Regel ist einfach: bis Sie ein Leistungsproblem mit dem React DevTools Profiler entdecken — verwenden Sie useCallback nicht. Das React-Team hat wiederholt betont, dass vorzeitige Optimierung die Wurzel allen Übels ist. Schreiben Sie zuerst sauberen Code ohne Memoization, messen Sie, finden Sie den Engpass im Profiler und fügen Sie dann useCallback dort hinzu, wo es wirklich nötig ist.

jsx
// Messbare Optimierung: Child ist in React.memo eingewickelt
const Child = React.memo(({ onClick }) => {
    console.log('Child neu gerendert');
    return <button onClick={onClick}>Click</button>;
});

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

Laut Dan Abramov — Before You memo() (2024) sind über 90% der useCallback-Verwendungen in Open-Source-Projekten überflüssig. Entwickler wickeln jede Funktion „nur für den Fall“ ein, ohne die Wirkung zu messen. Die Alternative: wenn die Kindkomponente schwer ist und ihre Neurenderung teuer — ist React.memo + useCallback gerechtfertigt. Wenn die Kindkomponente leicht ist — ist Neurendern billiger als Abhängigkeitsvergleich.

Wann man useCallback verwendet

Das erste Szenario ist React.memo. Wenn eine Kindkomponente in React.memo eingewickelt ist und eine Callback-Funktion als Prop erhält, wird die Kindkomponente ohne useCallback bei jedem Eltern-Rendering neu gerendert, selbst wenn sich ihre eigenen Daten nicht geändert haben. useCallback stabilisiert die Referenz, sodass React.memo die Neurenderung korrekt überspringen kann.

Das zweite Szenario ist useEffect mit einem Callback in den Abhängigkeiten. Wenn eine Funktion an das Abhängigkeiten-Array von useEffect übergeben wird, startet jede neue Referenz den Effekt neu. useCallback stellt sicher, dass die Referenz stabil ist, und der Effekt wird nur ausgeführt, wenn sich die tatsächlichen Daten ändern, nicht bei jedem Rendering. Dies ist besonders wichtig für Subskriptionen und Anfragen.

  • React.memo-Kinder — verhindert Neurenderungen von memoized Kindkomponenten beim Übergeben von Callbacks.
  • useEffect-Abhängigkeiten — stabilisiert die Funktion im Abhängigkeiten-Array des Effekts und verhindert unnötige Neustarts.
  • Benutzerdefinierte Hooks — wenn ein Hook einen Callback akzeptiert und von dessen Referenzgleichheit abhängt, ist useCallback erforderlich.
  • Kontextwert — wenn eine Funktion im Kontextwert übergeben wird, stabilisiert useCallback die Referenz.
jsx
// useCallback für stabile useEffect-Abhängigkeit
const fetchData = useCallback(async (id) => {
    const res = await fetch(`/api/${id}`);
    setData(res.data);
}, []); // stabile Referenz, wird nie neu erstellt

useEffect(() => {
    fetchData(props.id);
}, [props.id, fetchData]); // Effekt läuft nur, wenn sich props.id ändert

useCallback vs useMemo

Der Hauptunterschied zwischen useCallback und useMemo ist, was jeder memoized. useCallback memoized eine Funktion: useCallback(fn, deps) gibt fn zurück (dieselbe oder die vorherige Version). useMemo memoized das Ergebnis eines Funktionsaufrufs: useMemo(() => computeExpensive(a, b), [a, b]) gibt den berechneten Wert zurück, keine Funktion.

Technisch gesehen ist useCallback syntaktischer Zucker über useMemo: useCallback(fn, deps) ist äquivalent zu useMemo(() => fn, deps). Diese Syntax existiert nur zur Lesbarkeit — damit der Entwickler klar sieht, dass eine Funktion memoized wird, nicht ein Wert. Es gibt keinen Leistungsunterschied zwischen useCallback und useMemo mit einer Funktion — sie erzeugen identischen Code.

HookMemoizedSyntaxVerwendung
useCallbackFunktion (Referenz)useCallback(fn, deps)Callbacks für Kindkomponenten
useMemoBerechnungsergebnisuseMemo(() => value, deps)Teure Berechnungen, Objektmemoization
jsx
// Diese sind äquivalent:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);

Häufige Fehler mit useCallback

Der häufigste Fehler ist das sinnlose Einwickeln aller Funktionen in useCallback ohne React.memo auf Kindkomponenten. Wenn eine Kindkomponente nicht in React.memo eingewickelt ist, wird sie bei jedem Eltern-Rendering trotzdem neu gerendert, unabhängig davon, ob sich die Callback-Referenz ändert oder nicht. useCallback ohne React.memo ist Kosten ohne Nutzen.

  • useCallback ohne React.memo — Kindkomponente rendert trotzdem neu; Optimierung ist sinnlos.
  • Vergessene Abhängigkeiten — wenn eine im Callback verwendete Variable nicht in deps ist, enthält der Callback einen stale Closure.
  • Übermäßige Memoization — Einwickeln jeder Funktion, einschließlich trivialer onClick mit console.log.
  • Objekte und Arrays in Abhängigkeiten — eine neue Referenz bei jedem Rendering gilt als Änderung, Memoization ist nutzlos.
jsx
// ❓ Useless: kein React.memo am Kind
const handle = useCallback(() => doStuff(), []);
<Child onClick={handle} />; // Child rendert ohne React.memo trotzdem neu

// ❓ Stale Closure: fehlende Abhängigkeit
const handle = useCallback(() => {
    console.log(count); // count ist immer 0 — stale Closure!
}, []);

// ✅ Richtig: Abhängigkeiten einschließen
const handle = useCallback(() => {
    console.log(count);
}, [count]);

Das Problem des stale Closure in useCallback wird gelöst, indem alle verwendeten Variablen in das Abhängigkeiten-Array aufgenommen werden. eslint-plugin-react-hooks mit exhaustive-deps überprüft automatisch, dass alle Variablen aus dem Callback-Rumpf im Array vorhanden sind. Wenn der Callback setState verwendet, das sich zwischen Renderings nicht ändert, kann es sicher in deps aufgenommen werden — React garantiert die Stabilität von setState.

Häufig gestellte Fragen

Sollte jede Funktion in useCallback eingewickelt werden?

Nein. useCallback macht nur in drei Fällen Sinn: die Kindkomponente ist in React.memo eingewickelt, die Funktion wird im Abhängigkeiten-Array von useEffect verwendet, oder die Funktion wird an einen benutzerdefinierten Hook übergeben, der von Referenzgleichheit abhängt. In anderen Fällen fügt useCallback Overhead ohne Nutzen hinzu. Das React-Team empfiehlt, zuerst ohne Optimierungen zu schreiben und sie basierend auf Profiling-Ergebnissen hinzuzufügen.

Was ist schneller — useCallback oder eine neue Funktion bei jedem Rendering?

Für einfache Komponenten — eine neue Funktion bei jedem Rendering ist etwas schneller, da useCallback Ressourcen für den Abhängigkeitsvergleich und die Array-Allokation verbraucht. Für Komponenten mit tiefen React.memo-Bäumen gewinnt useCallback, indem es die Neurenderung von Tausenden von Kindelementen verhindert. Messen und vergleichen Sie, statt zu raten — verwenden Sie den React DevTools Profiler für eine objektive Bewertung.

Kann useCallback mit async-Funktionen verwendet werden?

Ja, useCallback funktioniert mit async-Funktionen genauso wie mit synchronen. Der Hook memoized die Funktion selbst, und das Ergebnis (ein Promise) wird bei jedem Aufruf zurückgegeben. Eine async-Funktion innerhalb von useCallback ist ein häufiges Muster für stabile Datenlade-Callbacks, die in useEffect verwendet werden: const fetchData = useCallback(async (id) => {...}, []).

Wie debuggt man Probleme mit useCallback und React.memo?

Verwenden Sie React DevTools Profiler — er zeigt, welche Komponenten neu rendern und warum. Für programmatische Überprüfung fügen Sie console.log hinzu oder verwenden Sie useWhyDidYouUpdate — eine Bibliothek, die den Grund für die Neurenderung protokolliert. Die Hauptgründe: eine Prop hat sich geändert (einschließlich Callback-Referenz), der State hat sich geändert oder der Kontext hat sich geändert. Wenn useCallback nicht hilft — überprüfen Sie, ob alle Abhängigkeiten korrekt angegeben sind.

Wie übergibt man useCallback an eine Kindkomponente ohne React.memo?

Auch ohne React.memo kann useCallback in Kombination mit useMemo für Kontextwerte nützlich sein. Wenn Sie ein Objekt mit Funktionen an Context.Provider übergeben, wickeln Sie die Objekterstellung in useMemo und jede Funktion in useCallback. Dies verhindert die Neurenderung aller Kontextkonsumenten, wenn sich eine der Funktionen ändert. Aber für die direkte Übergabe von Callbacks in Props ohne React.memo bringt useCallback keinen Nutzen.

Zusammenfassung

  • useCallback — ein Hook zum Memoieren einer Funktion, der eine stabile Referenz zurückgibt, bis sich Abhängigkeiten ändern.
  • React.memo-Synergie — useCallback ist nur mit React.memo auf Kindkomponenten wirksam.
  • Abhängigkeitsvergleich — über Object.is; Objekte/Arrays in deps brechen die Memoization.
  • Messen, nicht raten — useCallback erst nach Erkennung eines Engpasses im Profiler hinzufügen.
  • Stabiler Closure — alle verwendeten Variablen in das Abhängigkeiten-Array aufnehmen, sonst — stale Closure.
  • useCallback vs useMemo — useCallback memoized eine Funktion, useMemo memoized ein Berechnungsergebnis.
  • Übermäßiger Gebrauch — über 90% der useCallback-Verwendungen in echten Projekten sind vorzeitige Optimierung.

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch