aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Webentwicklung

React Performance optimieren: memo und useMemo

Die überwiegende Mehrheit der React Performance Probleme stammt nicht von schlechten Algorithmen, sondern von unnötigen Re-Renders. Jedes Mal, wenn eine Komponente rendert, führt sie ihren Teilbaum erneut aus; meist ist das günstig, doch bei großen Listen, schweren Berechnungen oder tiefen Bäumen beginnt die Oberfläche zu ruckeln. Dieser Artikel zeigt, wie man Renders misst und wie man sie mit dem Trio aus memo, useMemo und useCallback sinnvoll reduziert.

Warum rendert React erneut?

Eine Komponente rendert in drei Fällen neu: wenn sich ihr eigener state ändert, wenn sich die empfangenen props ändern, oder wenn ihre Elternkomponente rendert. Letzteres wird am häufigsten übersehen: Wenn ein Elternteil rendert, rendern seine Kinder standardmäßig neu, selbst wenn sich ihre Props nie geändert haben.

Meistens ist das kein Problem; Reacts Render ist reine JavaScript-Ausführung und berührt das echte DOM nur, wenn es einen tatsächlichen Unterschied gibt. Schwierig wird es, wenn der Render selbst teuer ist: eine Tabelle mit Tausenden von Zeilen, ein komplexes Diagramm oder eine schwere Schleife, die bei jedem Render neu berechnet wird.

Messen, bevor du optimierst

Verfrühte Optimierung ist die größte Falle. Bevor du memo überall verteilst, miss, wo es wirklich langsam ist. Der Reiter Profiler in der Erweiterung React Developer Tools ist genau dafür gemacht: Aufnahme starten, Interaktion ausführen, stoppen. Das Flame-Graph zeigt, welche Komponente wie oft und in wie vielen Millisekunden gerendert hat.

Aktiviere während der Entwicklung die Option „Record why each component rendered" des Profilers. Wenn du häufig den Rendergrund „Parent component rendered" siehst, hast du einen Kandidaten für Memoization gefunden.

React.memo, um eine Komponente zu merken

React.memo umhüllt eine Komponente und überspringt den Render, wenn sich ihre Props laut einem flachen (shallow) Vergleich nicht geändert haben. Ideal für reine, von Props gesteuerte Präsentationskomponenten.

import { memo } from 'react';

const UserRow = memo(function UserRow({ name, email }) {
  console.log('render', name);
  return (
    <tr>
      <td>{name}</td>
      <td>{email}</td>
    </tr>
  );
});

Nun rendert UserRow nicht neu, wenn das Elternteil rendert, solange name und email gleich bleiben. Aber Vorsicht: Der flache Vergleich vergleicht Objekt- und Funktions-Props per Referenz. Erzeugst du bei jedem Render ein neues Objekt oder eine neue Funktion, bringt memo nichts, weil sich die Referenz jedes Mal ändert.

useMemo, um teure Berechnungen zwischenzuspeichern

useMemo merkt sich einen Wert: Es führt die Berechnung nicht erneut aus, solange sich die angegebenen Abhängigkeiten nicht ändern. Es hat zwei Hauptanwendungen: das Überspringen wirklich teurer Berechnungen und das Stabilhalten einer Objekt-/Array-Referenz, die du an mit memo umhüllte Kinder übergibst.

const sortedUsers = useMemo(() => {
  return [...users].sort((a, b) => a.name.localeCompare(b.name));
}, [users]);

Hier läuft die Sortierung nur, wenn sich das Array users ändert; wenn ein anderer State im Elternteil einen Render auslöst, wird die Sortierung übersprungen. Wichtige Regel: Sei ehrlich mit dem Abhängigkeits-Array. Jede externe Variable, die in der Berechnung verwendet wird, muss darin stehen, sonst arbeitest du mit veralteten (stale) Werten.

Widerstehe der Versuchung, alles in useMemo zu wickeln. Der Vergleich und der Cache selbst kosten etwas; eine günstige Berechnung wie a + b zu memoizen ist ein Nettoverlust.

useCallback, um eine Funktionsreferenz zu stabilisieren

useCallback ist eigentlich die funktionsspezifische Form von useMemo: Es bewahrt die Referenz einer Funktion, bis sich die Abhängigkeiten ändern. Sein wahrer Wert zeigt sich, wenn du einen Callback an ein mit memo umhülltes Kind übergibst.

const handleDelete = useCallback((id) => {
  setUsers((prev) => prev.filter((u) => u.id !== id));
}, []);

// das memo-Kind rendert wegen dieser Prop nicht mehr neu
<UserRow user={user} onDelete={handleDelete} />

Mit einem leeren Abhängigkeits-Array wird die Funktion einmal beim ersten Render erstellt und behält immer dieselbe Referenz. Beachte, dass ich die funktionale (Updater-)Form von setUsers verwende — so vermeide ich, users zu den Abhängigkeiten hinzuzufügen, und halte den Callback stabil.

Häufige Fehler

  • Ein neues Objektliteral an eine memo-Komponente übergeben: style={{ margin: 8 }} ist bei jedem Render eine neue Referenz und hebelt die Memoization aus. Verlagere das konstante Objekt aus der Komponente oder erzeuge es mit useMemo.
  • Den Index als Listenschlüssel verwenden: In Listen mit Sortierung oder Einfügen/Löschen führt key={index} zu falschen Zuordnungen und unnötigen Renders; verwende eine stabile id.
  • Optimierung überall verteilen: Ohne Messung hinzugefügtes memo/useMemo schadet der Lesbarkeit und bringt keinen Nutzen.

Eine Anmerkung zu React 19 und dem Compiler

Der mit React 19 eingeführte React Compiler will den Großteil dieser Memoization automatisch zur Bauzeit erledigen; mit anderen Worten, der künftige Bedarf, useMemo/useCallback von Hand zu schreiben, könnte sinken. Dennoch ist der Compiler noch nicht in jedem Projekt aktiviert, und das Verständnis dieser Konzepte bleibt für bestehende Codebasen unerlässlich. Das Prinzip „erst messen, dann denken, zuletzt optimieren" gilt in jeder Version.

Häufige Fragen

Was ist der Unterschied zwischen memo, useMemo und useCallback?

memo umhüllt eine Komponente und überspringt deren Render, solange sich die Props nicht ändern. useMemo speichert einen Wert zwischen. useCallback hält eine Funktionsreferenz stabil; es ist eigentlich eine Kurzform für useMemo(() => fn, deps).

Sollte ich jede Komponente in memo wickeln?

Nein. Der Vergleich kostet ebenfalls etwas und ist bei günstigen Renders ein Nettoverlust. Memoize nur Komponenten, von denen du mit dem Profiler nachgewiesen hast, dass sie wirklich teuer sind oder sehr oft rendern.

Macht useMemo es wirklich schneller?

Nur wenn die Berechnung teuer genug ist. Bei einfacher Arithmetik oder kleinen Arrays können die Kosten des Caches den Nutzen übersteigen. Miss und entscheide.

Möchtest du deine React-Anwendung beschleunigen? Um deine Renders zu profilieren und Engpässe mit echten Messungen zu beheben, nimm Kontakt mit mir auf.

Bu kategorideki tüm yazılar →

Devamı için