Hirefullstack – Software Engineering & IT-Beratung aus Berlin
← Zur Übersicht
Kern 13 Min Lesezeit

useMemo & useCallback

Ergebnisse aufheben, statt sie neu auszurechnen

In einem Satz

Beide heben etwas zwischen den Durchläufen auf: useMemo ein Ergebnis, useCallback eine Funktion. Beide sind Optimierungen – kein Standardwerkzeug.

Bei jedem Zeichnen läuft deine Komponente komplett neu durch. Normalerweise ist das billig. Manchmal nicht – dann kannst du React sagen: „Solange sich diese Werte nicht ändern, nimm bitte das Ergebnis von vorhin.“ Das nennt man MemoisierungEin Ergebnis aufheben, statt es noch einmal auszurechnen.Nach dem Motto: „Solange sich die Zutaten nicht ändern, bleibt das Ergebnis gleich.“.

aufbau.ts
const ergebnis = useMemo(() => teuerRechnen(a, b), [a, b]);
//                        ▲ Funktion, die den Wert LIEFERT     ▲ worauf achten

const fn = useCallback((x) => machWas(x, a), [a]);
//                     ▲ die Funktion SELBST wird aufgehoben
Bildlich gesprochen

Ein Zettel am Kühlschrank: „Solange a und b gleich bleiben, ist das Ergebnis das hier.“ Ändert sich einer der beiden, wird neu gerechnet – sonst gibt es die alte Antwort.

Gut zu wissen useCallback ist nur eine Abkürzung

useCallback(fn, deps) ist exakt dasselbe wie useMemo(() => fn, deps). Es gibt ihn nur, weil man Funktionen so oft aufhebt.

Es gibt genau drei gute Gründe

gruende.txt
1. Die Berechnung ist wirklich teuer
   (Tausende Einträge sortieren, aufwendig formatieren)

2. Der Wert geht als Prop an eine Komponente mit React.memo
   -> ohne gleichbleibende Referenz greift memo nicht

3. Der Wert steht in einer Dependency-Liste (useEffect & Co.)
   -> sonst läuft der Effect bei jedem Zeichnen

Alles andere macht deinen Code nur länger und schwerer lesbar. useMemo um a + b kostet mehr, als es spart.

beispiel.tsx
function Liste({ items, filter, onWaehlen }) {
  // Grund 1: teuer, und das Ergebnis wird angezeigt
  const sichtbar = useMemo(
    () => items.filter((i) => i.name.includes(filter)).sort(vergleiche),
    [items, filter]
  );

  // Grund 2: geht an eine memo-Komponente
  const handleClick = useCallback((id) => onWaehlen(id), [onWaehlen]);

  return sichtbar.map((i) => <Zeile key={i.id} item={i} onClick={handleClick} />);
}

const Zeile = memo(function Zeile({ item, onClick }) {
  return <li onClick={() => onClick(item.id)}>{item.name}</li>;
});
Stolperstein useCallback allein bringt gar nichts

Eine gleichbleibende Funktion nützt nur, wenn jemand sie vergleicht. Gibst du sie an ein normales <button onClick={…}>, hast du nur Arbeit hinzugefügt. Der Button vergleicht nichts.

Was React.memo macht

React.memo ist eine Hülle um eine Komponente. Sie sagt: „Wenn alle Props dieselben sind wie letztes Mal, spar dir das Zeichnen.“ Der Vergleich ist flacher VergleichReact schaut nur, ob die Werte oberflächlich dieselben sind – nicht, ob zwei Objekte denselben Inhalt haben. – er schaut nicht in Objekte hinein.

memo.tsx
const Teuer = memo(function Teuer({ daten }) { … });

<Teuer daten={items} />                  // ✅ gleich, solange items gleich ist
<Teuer daten={items.filter(Boolean)} />  // ❌ jedes Mal ein neues Array
<Teuer style={{ color: "red" }} />       // ❌ jedes Mal ein neues Objekt
Tipp Oft ist Umbauen besser als Memoisieren

Ein Kind als children hereinreichen statt es innen zu zeichnen, verhindert dessen Neuzeichnen ganz ohne memo. Und State möglichst nah an die Stelle zu legen, die ihn braucht, wirkt genauso. Erst Struktur, dann Memo.

Nimm es, wenn …
  • +

    Berechnungen, die im ProfilerEin Messwerkzeug in den React DevTools, das zeigt, welche Komponente wie lange gebraucht hat. tatsächlich auffallen

  • +

    Props für Komponenten mit React.memo

  • +

    Werte, die in einer Dependency-Liste landen

Lass es, wenn …
  • useMemo um a + b, items.length oder ein kurzes map

  • Vorsorgliches Memoisieren „für später“

  • useCallback für Handler, die direkt an ein <button> gehen

Genauer erklärt: Kosten, Missverständnisse und der React Compiler optional
Achtung Auch Aufheben kostet

Jeder dieser Hooks belegt Speicher und vergleicht bei jedem Durchlauf seine Liste. Bei billigen Berechnungen ist das teurer als die Berechnung selbst.

Das häufigste Missverständnis:useMemo verhindert, dass die Komponente neu zeichnet.“ Tut es nicht. Die Komponente läuft trotzdem durch – nur die eine Berechnung darin wird übersprungen. Renders verhindert React.memo, und zwar bei Kind-Komponenten.

Stolperstein Nichts mit Wirkung nach außen hineinschreiben

Die Funktion in useMemo läuft mitten im Zeichnen. Dort darf kein fetch und kein setState stehen – React darf sie verwerfen oder wiederholen.

Gut zu wissen Ausblick: der React Compiler

Der React Compiler (seit 2025 in Version 1.0) setzt diese Optimierung automatisch ein. Wo er läuft, schreibst du useMemo und useCallback kaum noch von Hand. Verstehen musst du sie trotzdem – in bestehendem Code stehen sie überall.

Sitzt das schon?

4 Fragen zu dieser Lektion. Falsche Antworten landen in deiner Statistik.

Jetzt selbst schreiben

Nur neu rechnen, wenn es sein muss

Die Liste wird bei jedem Zeichnen neu gefiltert – auch wenn sich nur der Zähler geändert hat. Sorge dafür, dass nur eine Änderung im Suchfeld neu rechnen lässt. Die Zeile „Berechnungen“ zeigt dir, wie oft gerechnet wurde.

  • Filtern nach an zeigt nur passende Einträge
  • Ein Klick auf Zähler +1 löst keine neue Berechnung aus
  • Eine Änderung im Suchfeld löst eine neue Berechnung aus
import { useRef, useState } from "react";

const DATEN = ["Banane", "Ananas", "Mandarine", "Kirsche"];

export default function App() {
  const [filter, setFilter] = useState("");
  const [count, setCount] = useState(0);
  const rechnungen = useRef(0);

  // TODO: nur neu berechnen, wenn sich filter ändert
  rechnungen.current += 1;
  const sichtbar = DATEN.filter((d) =>
    d.toLowerCase().includes(filter.toLowerCase())
  );

  return (
    <div style={{ fontFamily: "system-ui", padding: 16 }}>
      <input
        aria-label="filter"
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        placeholder="filtern …"
      />{" "}
      <button onClick={() => setCount((c) => c + 1)}>Zähler +1 ({count})</button>
      <p data-testid="rechnungen">Berechnungen: {rechnungen.current}</p>
      <ul>
        {sichtbar.map((d) => (
          <li key={d}>{d}</li>
        ))}
      </ul>
    </div>
  );
}

Hirefullstack

Ihr braucht React-Verstärkung im Team?

Wir bauen seit Jahren React- und Next.js-Anwendungen für Kunden in ganz Deutschland – als einzelner Experte, als Verstärkung fürs Bestandsteam oder als komplettes Scrum-Team.

Projekt besprechen →