fuer resiliente React UIs, die sich selbst wieder erholen
Eine Error Boundary, die nach einem Fehler dauerhaft eine graue Wand zeigt, ist nur die halbe Loesung. Mit resetKeys, Retry Buttons und einem sauberen Reset Mechanismus wird aus dem Fallback ein echter Wiederherstellungspfad statt einer Sackgasse.
Inhaltsverzeichnis
- 1. Warum eine Error Boundary allein nicht reicht
- 2. Das resetKeys Pattern von react-error-boundary
- 3. Retry Button fuer manuelles Zuruecksetzen
- 4. Eine eigene Error Boundary Klasse ohne Bibliothek
- 5. Grenzen: Was Error Boundaries nicht abfangen
- 6. Granularitaet: Wo Boundaries platzieren
- 7. Fallback UI und UX Ueberlegungen
- 8. Fehler an Monitoring wie Sentry melden
- 9. Praxischeckliste und Vergleich der Ansaetze
- 10. Zusammenfassung
- 11. FAQ
1. Warum eine Error Boundary allein nicht reicht
Eine klassische Error Boundary in React faengt Rendering-Fehler in ihrem Kindbaum ab und zeigt statt eines weissen Bildschirms eine Fallback-UI. Das ist der Ausgangspunkt, aber in einer produktiven Anwendung reicht das nicht aus: Sobald der Fehler einmal aufgetreten ist, bleibt die Boundary dauerhaft im Fehlerzustand haengen, weil componentDidCatch den internen State einmalig setzt und React die Komponente danach nicht mehr automatisch neu rendert. Der Nutzer sieht eine Fehlermeldung und hat ohne einen kompletten Seiten-Reload keine Moeglichkeit, wieder in einen funktionierenden Zustand zu gelangen.
Resiliente UIs brauchen deshalb zwei zusaetzliche Faehigkeiten: einen expliziten Weg, den Fehlerzustand zurueckzusetzen, und einen Mechanismus, der bei bestimmten Aenderungen automatisch zuruecksetzt, etwa wenn sich die Route oder eine zentrale Prop aendert. Beide Anforderungen lassen sich mit reinem React umsetzen, werden aber durch die Bibliothek react-error-boundary von Kent C. Dodds deutlich komfortabler, weil sie das resetKeys Pattern und einen onReset Callback bereits mitbringt, statt dass man sie in jeder Boundary neu erfindet.
2. Das resetKeys Pattern von react-error-boundary
Die Komponente ErrorBoundary aus react-error-boundary akzeptiert eine Prop namens resetKeys: ein Array von Werten, das die Boundary bei jedem Render vergleicht. Aendert sich auch nur einer dieser Werte zwischen zwei Renders, setzt die Boundary ihren internen Fehlerzustand automatisch zurueck und versucht, die Kinder erneut zu rendern, ganz ohne dass der Nutzer aktiv etwas tun muss. Das ist besonders nuetzlich, wenn ein Fehler an einen konkreten Datensatz gebunden ist, etwa eine Produkt ID in einer Detailseite: Navigiert der Nutzer zu einem anderen Produkt, ist der alte Fehler nicht mehr relevant und die Boundary soll ihn nicht mehr anzeigen.
In der Praxis uebergibt man typischerweise die Werte, von denen der gerenderte Inhalt abhaengt, etwa die Produkt ID aus den URL Parametern oder einen Query Key. Wichtig dabei: resetKeys vergleicht die Werte oberflaechlich, sodass primitive Werte wie Strings oder Zahlen zuverlaessig funktionieren, waehrend neu erzeugte Objekte oder Arrays bei jedem Render als veraendert gelten und ungewollte Resets ausloesen koennen. Deshalb sollten resetKeys moeglichst aus stabilen, primitiven Identifikatoren bestehen und nicht aus Objekten, die bei jedem Render neu instanziiert werden.
import { ErrorBoundary } from 'react-error-boundary';
import { useParams } from 'react-router-dom';
function ProductErrorFallback({ error, resetErrorBoundary }) {
return (
<div role="alert" className="rounded-lg border border-red-200 bg-red-50 p-4">
<p className="font-semibold text-red-800">Produkt konnte nicht geladen werden</p>
<p className="text-sm text-red-700">{error.message}</p>
<button
onClick={resetErrorBoundary}
className="mt-2 rounded bg-red-600 px-3 py-1.5 text-sm text-white"
>
Erneut versuchen
</button>
</div>
);
}
function ProductDetailPage() {
const { productId } = useParams();
return (
<ErrorBoundary
FallbackComponent={ProductErrorFallback}
resetKeys={[productId]}
>
<ProductDetail productId={productId} />
</ErrorBoundary>
);
}
3. Retry Button fuer manuelles Zuruecksetzen
react-error-boundary reicht der Fallback-Komponente eine Funktion namens resetErrorBoundary durch. Ruft der Nutzer sie ueber einen Button auf, setzt die Boundary ihren internen Fehlerzustand zurueck und versucht, die Kinder erneut zu rendern, genau wie bei resetKeys, nur manuell ausgeloest statt automatisch. Das ist die richtige Wahl fuer Fehler, deren Ursache nicht an eine erkennbare Prop gebunden ist, etwa ein voruebergehender Netzwerkfehler beim Laden eines Widgets, bei dem der Nutzer selbst entscheiden soll, wann ein neuer Versuch sinnvoll ist.
Ein reiner Reset des UI-Zustands loest allerdings nicht automatisch die eigentliche Ursache: Wenn die Komponente beim ersten Render aus einem fehlerhaften Cache oder einer fehlgeschlagenen Anfrage einen Fehler geworfen hat, wuerde ein simpler Remount denselben Fehler sofort reproduzieren. Deshalb kombiniert man den Retry Button meist mit einer expliziten Refetch-Funktion und einem Retry-Zaehler, der nach einigen erfolglosen Versuchen die Fallback-UI aendert, etwa von 'Erneut versuchen' zu 'Bitte spaeter erneut versuchen', um dem Nutzer keine Endlosschleife anzubieten.
import { useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
function RetryFallback({ error, resetErrorBoundary }) {
const [attempts, setAttempts] = useState(0);
const maxAttempts = 3;
const handleRetry = () => {
setAttempts((a) => a + 1);
resetErrorBoundary();
};
return (
<div role="alert" className="rounded-lg border p-4">
<p>{error.message}</p>
{attempts < maxAttempts ? (
<button onClick={handleRetry}>Erneut versuchen</button>
) : (
<p className="text-sm text-gray-500">
Bitte lade die Seite in ein paar Minuten neu.
</p>
)}
</div>
);
}
function Widget({ refetch }) {
return (
<ErrorBoundary
FallbackComponent={RetryFallback}
onReset={() => refetch()}
>
<DataDrivenWidget />
</ErrorBoundary>
);
}
4. Eine eigene Error Boundary Klasse ohne Bibliothek
Error Boundaries lassen sich in React bis heute nur als Klassenkomponente implementieren, weil sie die Lifecycle-Methoden static getDerivedStateFromError und componentDidCatch benoetigen, fuer die es keine Hook-Entsprechung gibt. getDerivedStateFromError wird synchron beim Rendern aufgerufen und liefert den neuen State, der die Fallback-UI aktiviert, waehrend componentDidCatch danach fuer Nebeneffekte wie Logging zustaendig ist. Wer keine zusaetzliche Abhaengigkeit einfuehren will, kann diese beiden Methoden mit wenig Code selbst implementieren.
Fuer den Reset ohne Bibliothek hat sich der Key-Remount-Trick etabliert: Man haelt einen Zaehler im Elternzustand und uebergibt ihn als key an die Boundary beziehungsweise an ihre Kinder. Aendert sich der key, entfernt React die gesamte Instanz und erzeugt sie neu, wodurch auch der interne Fehlerzustand der Boundary garantiert verschwindet, ganz ohne eine eigene resetError-Methode auf der Klasse implementieren zu muessen. Der Trick ist simpel, aber wirksam, weil er sich auf Reacts eingebautes Reconciliation-Verhalten stuetzt statt auf eigene State-Logik.
import { Component } from 'react';
class SimpleErrorBoundary extends Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, info) {
console.error('Boundary caught:', error, info.componentStack);
}
render() {
if (this.state.hasError) {
return this.props.fallback(this.state.error);
}
return this.props.children;
}
}
function Parent() {
const [resetCount, setResetCount] = useState(0);
return (
<SimpleErrorBoundary
key={resetCount}
fallback={(error) => (
<button onClick={() => setResetCount((c) => c + 1)}>
Neu laden ({error.message})
</button>
)}
>
<RiskyChild />
</SimpleErrorBoundary>
);
}
5. Grenzen: Was Error Boundaries nicht abfangen
Error Boundaries fangen ausschliesslich Fehler ab, die waehrend des Renderns, in Lifecycle-Methoden oder in Konstruktoren von Komponenten unterhalb der Boundary geworfen werden. Nicht abgefangen werden Fehler in Event Handlern wie onClick, Fehler in asynchronem Code wie setTimeout-Callbacks oder Promise-Ketten, Fehler beim serverseitigen Rendering und Fehler, die innerhalb der Boundary selbst auftreten. Ein try/catch in einem fetch-Callback, das nur die Konsole beschreibt, wird also niemals die Fallback-UI der umgebenden Boundary auslösen, egal wie die Boundary konfiguriert ist.
Um solche asynchronen Fehler dennoch ueber dieselbe Boundary sichtbar zu machen, nutzt man den 'throw in render' Trick: Man haelt den Fehler in einem State und wirft ihn beim naechsten Render explizit, sodass React ihn wie einen regulaeren Render-Fehler behandelt und die naechste Error Boundary greift. react-error-boundary bietet dafuer den Hook useErrorHandler beziehungsweise in neueren Versionen ein aehnliches Pattern, das genau diese Bruecke zwischen imperativem async Code und deklarativer Fehlerbehandlung schlaegt.
import { useState, useCallback } from 'react';
function useThrowAsyncError() {
const [, setError] = useState();
return useCallback((error) => {
setError(() => {
throw error;
});
}, []);
}
function AsyncWidget() {
const throwAsyncError = useThrowAsyncError();
useEffect(() => {
fetchData().catch((err) => throwAsyncError(err));
}, [throwAsyncError]);
return <DataView />;
}
6. Granularitaet: Wo Boundaries platzieren
Eine einzige Error Boundary rund um die gesamte App ist schnell eingebaut, hat aber einen entscheidenden Nachteil: Ein Fehler in einem einzelnen, unwichtigen Widget wie einer Produktempfehlung reisst dann die komplette Seite mit in den Fehlerzustand, obwohl der Rest der Anwendung problemlos funktionieren wuerde. Am anderen Ende des Spektrums steht eine Boundary um jede einzelne Komponente, was zwar maximale Isolation bietet, aber massiv Boilerplate erzeugt und dazu fuehrt, dass zusammengehoerige UI-Bereiche unabhaengig voneinander in Fehlerzustaende fallen koennen, was fuer den Nutzer verwirrend wirkt.
In der Praxis bewaehrt sich eine zweistufige Strategie: eine grobe, applikationsweite Boundary am Wurzelknoten als letzte Sicherheitsnetz-Instanz, kombiniert mit gezielten Boundaries um in sich geschlossene Feature-Widgets wie einen Warenkorb, eine Kommentarsektion oder ein Empfehlungs-Karussell. Jede dieser inneren Boundaries bekommt eigene resetKeys, die zum Datenkontext des jeweiligen Widgets passen, sodass ein Fehler im Empfehlungs-Widget niemals den Checkout-Bereich derselben Seite lahmlegt.
7. Fallback UI und UX Ueberlegungen
Die Fallback-UI sollte dem Nutzer klar kommunizieren, ob es sich um ein voruebergehendes Problem handelt, das ein erneuter Versuch loesen kann, oder um einen dauerhaften Fehler, bei dem ein Retry Button nur Frustration erzeugt. Technische Details wie Stacktraces gehoeren in Produktion nicht in die sichtbare UI, sondern in ein separates Logging-System, waehrend die sichtbare Meldung verstaendlich und handlungsorientiert bleibt, etwa 'Dieser Bereich konnte nicht geladen werden' statt einer rohen Fehlermeldung aus der JavaScript Engine.
Layout-technisch sollte die Fallback-UI moeglichst dieselbe Groesse einnehmen wie der eigentliche Inhalt, damit umliegende Elemente nicht springen, wenn der Fehler auftritt oder behoben wird. Fuer Barrierefreiheit ist ein role='alert' auf dem Fallback-Container wichtig, damit Screenreader die Fehlermeldung automatisch ankuendigen, und bei kritischen Fehlern kann zusaetzlich der Fokus explizit auf die Fehlermeldung gesetzt werden, damit Tastaturnutzer sie nicht uebersehen.
8. Fehler an Monitoring wie Sentry melden
componentDidCatch liefert neben dem Fehlerobjekt auch ein info-Objekt mit componentStack, das genau zeigt, in welchem Komponentenbaum der Fehler aufgetreten ist, was fuer die Fehlersuche oft wertvoller ist als der reine JavaScript-Stacktrace. Diese Information sollte an ein Monitoring-System wie Sentry weitergereicht werden, idealerweise angereichert um Kontext wie die aktuellen resetKeys, die Retry-Anzahl und relevante IDs, damit sich Haeufungen bestimmter Fehler bestimmten Datensaetzen zuordnen lassen.
react-error-boundary bietet dafuer die Prop onError auf der ErrorBoundary-Komponente, die zentral an einer Stelle im Code das Reporting uebernimmt, statt es in jeder einzelnen Boundary zu wiederholen. Wichtig ist dabei, Wiederholungen zu deduplizieren: Wenn ein Nutzer denselben Fehler durch mehrfaches Klicken auf Retry mehrfach ausloest, sollte das Monitoring nicht jedes Mal ein separates, gleichwertiges Ereignis erzeugen, sondern die Retry-Anzahl als zusaetzliches Feld am urspruenglichen Ereignis fuehren.
import * as Sentry from '@sentry/react';
import { ErrorBoundary } from 'react-error-boundary';
function App() {
return (
<ErrorBoundary
FallbackComponent={AppFallback}
onError={(error, info) => {
Sentry.captureException(error, {
contexts: { react: { componentStack: info.componentStack } },
tags: { boundary: 'app-root' },
});
}}
>
<Router />
</ErrorBoundary>
);
}
9. Praxischeckliste und Vergleich der Ansaetze
Fuer kleine, isolierte Anwendungen ohne externe Abhaengigkeiten kann eine selbst geschriebene Boundary mit Key-Remount ausreichen, weil sie ohne zusaetzliches Paket auskommt und der Code vollstaendig unter eigener Kontrolle bleibt. Sobald aber mehrere Boundaries mit unterschiedlichen resetKeys, Retry-Zaehlern und zentralem Error-Reporting gebraucht werden, zahlt sich react-error-boundary aus, weil es diese Muster bereits robust und gut getestet implementiert, statt dass jedes Team sie erneut nachbaut.
Die folgende Tabelle fasst zusammen, welcher Ansatz zu welchem Anwendungsfall passt. Als Faustregel gilt: automatischer Reset ueber resetKeys fuer datenabhaengige Fehler, manueller Retry Button fuer netzwerkbedingte Fehler, und eine Kombination aus beidem fuer Widgets, die sowohl von externen Daten als auch von instabilen Verbindungen abhaengen.
| Ansatz | Automatischer Reset | Aufwand | Einsatzgebiet |
|---|---|---|---|
| Eigene Klasse ohne Reset | Nein |
Niedrig | Prototypen, einmalige Fallbacks |
| Eigene Klasse mit Key-Remount | Nein, manuell ueber Key |
Mittel | Kleine Apps ohne Extra-Paket |
| react-error-boundary ohne resetKeys | Nein, nur Retry Button |
Niedrig | Netzwerk-abhaengige Widgets |
| react-error-boundary mit resetKeys | Ja, bei Prop-Aenderung |
Niedrig | Detailseiten mit wechselnder ID |
| react-error-boundary mit onReset und Retry-Zaehler | Ja, plus begrenzte manuelle Versuche |
Mittel | Kritische Widgets mit Monitoring |
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
Error Boundary Retry Patterns: Das Wichtigste auf einen Blick
resetKeys
Array von Werten, bei dessen Aenderung die Boundary automatisch zuruecksetzt, ideal fuer datenabhaengige Fehler wie wechselnde IDs.
Retry Button
resetErrorBoundary manuell aufrufen, kombiniert mit Refetch und einem Versuchszaehler gegen Endlosschleifen.
Key-Remount
Eigene Boundary ohne Bibliothek per key-Aenderung am Elternzustand zuruecksetzen, nutzt Reacts Reconciliation.
Monitoring
componentStack aus componentDidCatch an Sentry oder aehnliche Tools senden, Retry-Anzahl als Kontext mitfuehren.