ohne die Bibliothek why-did-you-render zu installieren
Nicht jede zusaetzliche npm-Abhaengigkeit ist noetig, um herauszufinden, warum eine Komponente zu oft neu rendert. Die Highlight-Updates-Option der React DevTools und ein zehn Zeilen langer eigener Hook reichen fuer die meisten Faelle bereits aus.
Inhaltsverzeichnis
- 1. Wozu ueberhaupt why-did-you-render und wann man es nicht braucht
- 2. Highlight Updates in den React DevTools aktivieren
- 3. Wo Highlight Updates an seine Grenzen stoesst
- 4. Ein eigener useWhyDidYouUpdate Hook
- 5. Den Hook in der Praxis anwenden
- 6. Den Hook um State und Context erweitern
- 7. Alternative: React Scan und aehnliche moderne Tools
- 8. Den Hook wieder sauber entfernen
- 9. Vergleich der Debugging-Ansaetze
- 10. Zusammenfassung
- 11. FAQ
1. Wozu ueberhaupt why-did-you-render und wann man es nicht braucht
Die Bibliothek why-did-you-render patcht React zur Laufzeit und protokolliert in der Konsole automatisch, wann und warum eine Komponente neu gerendert wird, inklusive eines Vergleichs der alten und neuen Props oder des alten und neuen State. Das ist praktisch fuer grosse Codebasen, in denen viele Komponenten gleichzeitig ueberwacht werden sollen, bringt aber eine zusaetzliche Abhaengigkeit, einen Monkeypatch von React selbst und einen gewissen Konfigurationsaufwand mit sich, der sich nicht fuer jede Debugging-Sitzung lohnt.
Fuer den haeufigeren Fall, dass man bereits eine konkrete Komponente im Verdacht hat, zu oft zu rendern, reichen zwei deutlich leichtgewichtigere Werkzeuge: die in den React DevTools eingebaute Highlight-Updates-Funktion fuer eine schnelle visuelle Kontrolle, und ein selbst geschriebener useWhyDidYouUpdate Hook fuer eine praezise Konsolenausgabe genau in der betroffenen Komponente, ganz ohne zusaetzliches Paket im package.json.
2. Highlight Updates in den React DevTools aktivieren
Im Components-Tab der React DevTools befindet sich ein Zahnrad-Symbol, das ein Einstellungsmenue oeffnet, in dem sich die Option 'Highlight updates when components render' aktivieren laesst. Ist sie aktiv, umrandet React DevTools jede Komponente kurz farbig, sobald sie neu rendert, waehrend man ganz normal mit der Anwendung interagiert, ganz ohne eine formale Profiler-Aufnahme starten zu muessen. Das macht sofort sichtbar, welche Bereiche der Seite bei einer bestimmten Interaktion aufleuchten, selbst wenn man noch gar nicht weiss, welche Komponente man eigentlich verdaechtigen sollte.
Die Farbe des Rahmens gibt dabei eine grobe Einschaetzung der Render-Haeufigkeit: Blaue Rahmen erscheinen bei seltenen Renders, gruene bis gelbe bei haeufigeren, und ein Bereich, der bei jedem einzelnen Tastendruck in einem entfernten Suchfeld aufblitzt, ist ein starkes Indiz fuer eine fehlende Memoisierung oder eine zu grob gefasste Context-Abhaengigkeit. Diese Methode liefert keine Zahlen und keine Ursachenanalyse, ist aber der schnellste erste Schritt, um ueberhaupt herauszufinden, wo man mit einer detaillierteren Untersuchung ansetzen sollte.
3. Wo Highlight Updates an seine Grenzen stoesst
Highlight Updates zeigt zuverlaessig das 'Dass' eines Renders, aber nicht das 'Warum': Man sieht, dass eine Komponente aufleuchtet, aber nicht, ob sich Props, State oder Context veraendert haben und welcher konkrete Wert dafuer verantwortlich war. Bei tief verschachtelten Komponentenbaeumen mit vielen gleichzeitig aufleuchtenden Bereichen wird es zudem schnell unuebersichtlich, welches Aufleuchten die eigentliche Ursache und welches nur eine Folge eines Renders weiter oben im Baum ist.
Fuer diese Faelle braucht es eine Methode, die direkt in der Konsole ausgibt, welche konkreten Werte sich zwischen zwei Renders geaendert haben. Genau diese Luecke fuellt ein kleiner selbst geschriebener Hook, der sich wie ein Debugging-Statement in genau die eine Komponente einbauen laesst, die man gerade untersucht, und danach wieder entfernt wird, ohne dass er jemals in Produktion landet.
4. Ein eigener useWhyDidYouUpdate Hook
Der Kerngedanke ist simpel: Man haelt die Props des vorherigen Renders in einer Ref, vergleicht sie beim naechsten Render mit den aktuellen Props und gibt ueber console.log genau die Eigenschaften aus, die sich unterscheiden. Ein useEffect, der nach jedem Render laeuft, ist dafuer ausreichend, weil er garantiert nach dem Rendern und nach dem Aktualisieren der Ref ausgefuehrt wird, sodass beim naechsten Durchlauf immer der Vergleich zum tatsaechlich vorherigen Zustand stattfindet.
Der Hook nimmt als Parameter den Namen der Komponente fuer die Konsolenausgabe und das komplette Props-Objekt entgegen und vergleicht jede einzelne Eigenschaft per Referenzgleichheit (===). Fuer primitive Werte wie Strings oder Zahlen ist das ausreichend, fuer Objekte und Arrays zeigt der Vergleich zuverlaessig genau das Problem, das ohnehin meist die eigentliche Ursache unnoetiger Renders ist: eine bei jedem Elternrender neu erzeugte Referenz, auch wenn der Inhalt unveraendert bleibt.
import { useEffect, useRef } from 'react';
function useWhyDidYouUpdate(name, props) {
const previousProps = useRef();
useEffect(() => {
if (previousProps.current) {
const allKeys = Object.keys({ ...previousProps.current, ...props });
const changedProps = {};
allKeys.forEach((key) => {
if (previousProps.current[key] !== props[key]) {
changedProps[key] = {
from: previousProps.current[key],
to: props[key],
};
}
});
if (Object.keys(changedProps).length) {
console.log('[why-did-you-update]', name, changedProps);
}
}
previousProps.current = props;
});
}
function ExpensiveList(props) {
useWhyDidYouUpdate('ExpensiveList', props);
// ... eigentliche Render-Logik
}
5. Den Hook in der Praxis anwenden
Um den Hook einzusetzen, reicht ein einziger zusaetzlicher Aufruf am Anfang der verdaechtigen Komponente, direkt mit den bereits vorhandenen Props als zweitem Argument. Rendert die Komponente daraufhin unerwartet neu, erscheint in der Browser-Konsole eine Zeile, die exakt auflistet, welche Props sich vom vorherigen Wert zum neuen Wert veraendert haben, inklusive der alten und der neuen Referenz, was bei Funktionen und Objekten sofort zeigt, ob es sich um dieselbe Referenz oder eine neue handelt.
Besonders aufschlussreich ist der Fall, in dem die Konsole eine Eigenschaft als veraendert meldet, obwohl beide geloggten Werte inhaltlich identisch aussehen, etwa zwei Objekte mit denselben Feldwerten. Das ist der klassische Hinweis auf fehlende Memoisierung im Elternteil: Ein Objekt oder eine Callback-Funktion wird bei jedem Render der Elternkomponente neu erzeugt, obwohl sich der Inhalt nie aendert, und genau dieses Muster sollte man dann mit useMemo oder useCallback im Elternteil beheben, nicht in der Kindkomponente selbst.
6. Den Hook um State und Context erweitern
Die gezeigte Grundversion vergleicht ausschliesslich Props, aber unnoetige Renders koennen genauso gut durch internen State oder durch Context-Updates ausgeloest werden. Der Hook laesst sich problemlos erweitern, indem man ihm neben den Props zusaetzlich ein Objekt mit den relevanten State-Werten uebergibt, das intern genauso behandelt wird wie die Props, nur unter einem eigenen Namensraum in der Ausgabe, damit beide Quellen in der Konsole klar unterscheidbar bleiben.
Fuer Context-Updates ist die Erweiterung etwas subtiler, weil ein Context-Wert nicht als Prop hereinkommt, sondern per useContext direkt in der Komponente gelesen wird. Hier hilft es, den gelesenen Context-Wert einfach als zusaetzliches Feld in dasselbe Vergleichs-Objekt zu packen, das an den Hook uebergeben wird, sodass ein Context-bedingter Render genauso sichtbar wird wie ein durch Props ausgeloester.
function useWhyDidYouUpdate(name, values) {
const previous = useRef();
useEffect(() => {
if (previous.current) {
const changed = {};
Object.keys(values).forEach((key) => {
if (previous.current[key] !== values[key]) {
changed[key] = { from: previous.current[key], to: values[key] };
}
});
if (Object.keys(changed).length) {
console.log('[why-did-you-update]', name, changed);
}
}
previous.current = values;
});
}
function CartWidget(props) {
const [isOpen, setIsOpen] = useState(false);
const theme = useContext(ThemeContext);
useWhyDidYouUpdate('CartWidget', { ...props, isOpen, theme });
// ... eigentliche Render-Logik
}
7. Alternative: React Scan und aehnliche moderne Tools
Neben why-did-you-render und den DevTools-eigenen Bordmitteln gibt es inzwischen auch moderne, sehr leichtgewichtige Alternativen wie react-scan, die aehnlich wie Highlight Updates funktionieren, aber mit deutlich weniger Konfiguration und ohne dass React selbst gepatcht werden muss. Solche Tools eignen sich gut fuer eine schnelle Uebersicht ueber eine ganze Seite, ersetzen aber nicht die praezise Analyse, die der eigene Hook fuer eine einzelne, bereits identifizierte Komponente liefert.
Fuer den in diesem Artikel beschriebenen Anwendungsfall, das gezielte Debuggen einer bereits verdaechtigten Komponente ohne zusaetzliche Abhaengigkeit, bleibt die Kombination aus Highlight Updates fuer die grobe Lokalisierung und dem eigenen Hook fuer die praezise Ursachenanalyse jedoch der pragmatischste Weg, weil beide Werkzeuge bereits vorhanden sind oder mit minimalem Aufwand selbst geschrieben werden koennen.
8. Den Hook wieder sauber entfernen
Da der Hook ausschliesslich fuer die Fehlersuche gedacht ist, sollte er nach Abschluss der Untersuchung wieder aus der Komponente entfernt werden, statt dauerhaft im Code zu verbleiben und bei jedem Render unnoetigen console.log-Output zu erzeugen. Wer den Hook haeufiger braucht, kann ihn in einer eigenen Utility-Datei ablegen, die ausschliesslich in Development Builds importiert wird, etwa ueber eine Bedingung auf process.env.NODE_ENV, damit er in Produktion garantiert keine Wirkung entfaltet.
Alternativ laesst sich der Hook so gestalten, dass er im Produktions-Build zu einer No-Op-Funktion wird, die effektiv nichts tut, sodass er theoretisch im Code verbleiben koennte, ohne Schaden anzurichten. In der Praxis ist es dennoch sauberer, das Debugging-Statement nach getaner Arbeit aktiv zu entfernen, damit der Code langfristig lesbar bleibt und niemand raetseln muss, warum eine Komponente einen ungewoehnlichen Hook-Aufruf enthaelt.
9. Vergleich der Debugging-Ansaetze
Alle drei vorgestellten Ansaetze, Highlight Updates, der eigene useWhyDidYouUpdate Hook und die Bibliothek why-did-you-render, haben ihre Berechtigung, je nachdem, wie gross die Codebasis ist und wie praezise die Ursachenanalyse sein muss. Fuer eine erste Orientierung ohne jede Vorbereitung ist Highlight Updates unschlagbar schnell, fuer eine gezielte, wiederholbare Untersuchung einer einzelnen Komponente ist der eigene Hook meist die richtige Wahl.
Die Bibliothek why-did-you-render lohnt sich vor allem dann, wenn dauerhaft und ueber viele Komponenten hinweg auf unnoetige Renders geachtet werden soll, etwa als Teil eines groesseren Performance-Audits, waehrend die beiden anderen Ansaetze fuer punktuelle, gezielte Debugging-Sessions gedacht sind und danach wieder aus dem Code verschwinden.
| Ansatz | Zusaetzliche Abhaengigkeit | Zeigt Ursache | Am besten fuer |
|---|---|---|---|
| Highlight Updates (DevTools) | Nein |
Nein, nur dass gerendert wurde | Schnelle visuelle Erstorientierung |
| useWhyDidYouUpdate (eigener Hook) | Nein |
Ja, genaue Prop/State-Diffs | Gezielte Untersuchung einer Komponente |
| react-scan | Ja, aber leichtgewichtig |
Teilweise | Ueberblick ueber ganze Seiten |
| why-did-you-render (Bibliothek) | Ja, patcht React |
Ja, mit Diff-Ausgabe | Dauerhaftes Monitoring in Entwicklung |
Mironsoft
React-Architektur, Performance und Magento-Frontend-Integration
React-Frontends, die schnell bleiben statt mit jedem Feature langsamer zu werden?
Wir prüfen bestehende React-Anwendungen auf unnötige Re-Renders, aufgeblähte Bundles und fragile State-Verwaltung und bauen daraus ein Frontend, das performant bleibt und sich sauber an Magento oder andere Backends anbindet.
Performance-Audit
Re-Renders, Bundle-Größe und Ladezeiten systematisch messen und beheben.
State-Architektur
Context, Zustand und Server State sauber trennen statt alles in einen Topf zu werfen.
Magento-Integration
GraphQL- oder REST-Anbindung an Magento robust und typsicher aufbauen.
10. Zusammenfassung
Re-Render Debugging ohne Drittanbieter: Das Wichtigste auf einen Blick
Highlight Updates
DevTools-Option, faerbt rendernde Komponenten waehrend normaler Nutzung ein, ohne formale Aufnahme.
useWhyDidYouUpdate
Kleiner eigener Hook mit useRef und useEffect, loggt exakt die veraenderten Props zwischen zwei Renders.
Referenzgleichheit
Der Hook vergleicht per ===, wodurch neu erzeugte Objekte und Funktionen als Ursache sichtbar werden.
Kein npm-Paket noetig
Beide Techniken kommen ohne zusaetzliche Abhaengigkeit oder React-Patch aus.