Granica błędów (error boundary) to komponent Reacta, który łapie błędy rzucone podczas renderowania komponentów w jego środku i zamiast wywracać całą aplikację, pokazuje zastępczy UI. Musi być komponentem klasowym, który definiuje static getDerivedStateFromError, bo nie ma do tego hooka. Piszesz ją raz i owijasz nią dowolną część drzewa.
Naciśnij "Break the profile", żeby dziecko rzuciło błąd podczas renderowania.
Profil zostaje zastąpiony widokiem zastępczym, a reszta strony dalej działa. Konsola pokazuje błąd dwa razy: React wypisuje każdy złapany błąd przez console.error, a componentDidCatch wypisuje go ponownie. "Try again" czyści przyczynę (broken) i błąd granicy, więc profil renderuje się ponownie. Usuń this.props.onReset(); z reset, a Try again od razu wróci do widoku zastępczego, bo nic nie naprawiło przyczyny.
Jak działa klasa
Dwie metody robią z klasy granicę błędów:
static getDerivedStateFromError(error)wykonuje się podczas renderowania, zaraz po tym, jak dziecko rzuci błąd. Zwraca aktualizację stanu, która przełącza granicę na widok zastępczy. Musi być czysta: żadnego logowania, żadnych żądań.componentDidCatch(error, info)wykonuje się po tym, jak widok zastępczy jest na ekranie. Używaj jej do efektów ubocznych, takich jak wysłanie błędu do serwisu zgłaszania błędów.info.componentStackwymienia komponent, który rzucił błąd, i komponenty nad nim.
class ErrorBoundary extends Component {
state = { error: null };
static getDerivedStateFromError(error) {
return { error }; // switch to the fallback
}
componentDidCatch(error, info) {
reportError(error, info.componentStack); // your logging service
}
render() {
if (this.state.error) return this.props.fallback;
return this.props.children;
}
}
Nie ma odpowiednika w postaci hooka, więc nawet aplikacja napisana wyłącznie z komponentów funkcyjnych potrzebuje tej jednej klasy. Strona o komponentach klasowych wyjaśnia składnię klas, jeśli jest dla ciebie nowa.
Co łapie granica, a czego nie
Granica łapie błędy, które występują, gdy React pracuje nad komponentami w jej środku:
- renderowanie (ciało komponentu funkcyjnego,
renderklasy), - metody cyklu życia i konstruktory dzieci będących komponentami klasowymi,
- efekty (
useEffect,useLayoutEffect) i ref callbacki, - odrzuconą obietnicę odczytaną przez use, a w React 19 błąd rzucony przez akcję formularza albo funkcję przekazaną do
startTransitionzuseTransition.
Nie łapie:
- błędów w handlerach zdarzeń,
- kodu asynchronicznego, który uruchamiasz sam:
setTimeout, callbacków.then(),awaitw handlerze zdarzenia, - błędów we własnym
rendergranicy (łapie je następna granica wyżej), - błędów podczas renderowania na serwerze.
Handlery zdarzeń są pominięte, bo nie psują renderowania. Ekran wciąż jest w poprawnym stanie, więc wystarczy try/catch w handlerze. Jeśli chcesz, żeby pokazała się granica, zapisz błąd w stanie i rzuć go przy następnym renderowaniu:
Pierwszy przycisk zostawia przyciski na ekranie i tylko wypisuje w konsoli niezłapany błąd. Drugi łapie odrzucenie, zapisuje je, a granica przejmuje kontrolę.
Gdzie umieszczać granice
Jedna granica wokół całej aplikacji zatrzymuje biały ekran, ale wtedy każdy mały błąd zastępuje wszystko. Umieszczaj granice wokół części, które mogą zawieść niezależnie: widżetu w panelu bocznym, wykresu, każdej trasy. Awaria kosztuje wtedy użytkownika jeden panel, a nie całą stronę.
Tylko panel Stocks pokazuje widok zastępczy. Przenieś oba widżety do jednego <ErrorBoundary>, a działający panel Weather zniknie razem z nim.
Bez żadnej granicy React usuwa z ekranu całe drzewo, gdy błąd renderowania nie zostanie złapany. Pusta strona jest bezpieczniejsza niż zepsuta, która pokazuje złe dane, ale użytkownicy odbierają ją jako awarię.
Biblioteka react-error-boundary
Pakiet react-error-boundary opakowuje klasę za ciebie i dodaje pomocniki do resetowania. Nie jest dostępny w tym edytorze, więc pokazujemy go w statycznym bloku:
import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary';
function Fallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>Something went wrong: {error.message}</p>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
<ErrorBoundary FallbackComponent={Fallback} onReset={() => refetch()} resetKeys={[userId]}>
<Profile userId={userId} />
</ErrorBoundary>;
// Inside a child: hand an async error to the boundary
const { showBoundary } = useErrorBoundary();
fetchData().catch(showBoundary);
resetKeys resetuje granicę, gdy zmieni się którakolwiek z wymienionych wartości, a showBoundary wykonuje sztuczkę „zapisz i rzuć" z wcześniejszego przykładu.
Zgłaszanie błędów w React 19
React 19 zgłasza błędy w nowy sposób. Błędy złapane przez granicę są wypisywane raz przez console.error, a niezłapane trafiają do window.reportError. Żeby wysyłać je do własnego serwisu, przekaż opcje do createRoot. Każda opcja zastępuje odpowiadające jej domyślne logowanie:
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'), {
onCaughtError(error, errorInfo) {
// an error boundary caught it
logToService('caught', error, errorInfo.componentStack);
},
onUncaughtError(error, errorInfo) {
// no boundary caught it, and React removed the tree
logToService('uncaught', error, errorInfo.componentStack);
},
onRecoverableError(error, errorInfo) {
// React recovered by itself, for example after a hydration mismatch
logToService('recovered', error, errorInfo.componentStack);
},
});
root.render(<App />);
onRecoverableError służy do błędów, które React naprawił bez twojej pomocy, takich jak niezgodność hydracji albo błąd renderowania, który zniknął, gdy React spróbował ponownie. hydrateRoot przyjmuje te same trzy opcje.
Częste błędy
Umieszczanie granicy wewnątrz komponentu, który rzuca błąd. Granica łapie tylko błędy swoich dzieci. Jeśli Profile renderuje <ErrorBoundary> wokół własnego JSX, a potem rzuca błąd we własnym ciele, błąd przechodzi obok do następnej granicy wyżej. Owijaj komponent z poziomu jego rodzica.
Efekty uboczne w getDerivedStateFromError. Wykonuje się podczas renderowania i może wykonać się więcej niż raz, więc niech tylko zwraca stan. Logowanie należy do componentDidCatch albo do onCaughtError w roocie.
Reset, który niczego nie naprawia. Wyczyszczenie błędu granicy renderuje te same dzieci z tymi samymi propsami. Jeśli znowu rzucą błąd, użytkownik wraca do widoku zastępczego. Najpierw zmień to, co spowodowało błąd (pobierz dane ponownie, wyczyść złe dane wejściowe, zmień key), a potem wyczyść błąd, jak robi to pierwszy przykład przez onReset.
Jedna granica dla całej aplikacji i nic więcej. Zapobiega pustej stronie, ale każdą małą awarię zamienia w błąd całej strony. Dodaj granice wokół funkcji, a tę na najwyższym poziomie zostaw jako ostatnią deskę ratunku.
Widoki zastępcze, które ukrywają błąd przed tobą. Przyjazny komunikat jest właściwy dla użytkowników, ale upewnij się, że błąd i tak trafia do twoich logów. React 19 domyślnie wypisuje złapane błędy w konsoli; na produkcji ta konsola należy do twojego użytkownika, więc wysyłaj błędy do serwisu z componentDidCatch albo onCaughtError.
Granice błędów naturalnie łączą się z <Suspense>: Suspense obsługuje „jeszcze niegotowe", a granica „nie udało się". Umieść granicę na zewnątrz Suspense, jak w przykładach z use, żeby jeden komponent obejmował ładowanie, sukces i niepowodzenie.
Najczęściej zadawane pytania
Co to jest error boundary w React?
Komponent, który łapie błędy JavaScript rzucone podczas renderowania dowolnego komponentu w jego środku i zamiast tego renderuje zastępczy UI. Bez niego błąd podczas renderowania usuwa z ekranu całą aplikację.
Czy istnieje hook dla granic błędów?
Nie. Granica błędów musi być komponentem klasowym ze static getDerivedStateFromError (i opcjonalnie componentDidCatch). Piszesz ją raz i używasz z komponentów funkcyjnych jak każdego innego komponentu albo instalujesz react-error-boundary, który opakowuje klasę za ciebie.
Czy granice błędów łapią błędy w handlerach zdarzeń?
Nie. Handlery zdarzeń wykonują się poza renderowaniem, więc React nie potrzebuje granicy, żeby się podnieść. Użyj try/catch w handlerze, a jeśli chcesz, żeby pokazała się granica, zapisz błąd w stanie i rzuć go podczas renderowania.
Czy granice błędów łapią błędy asynchroniczne?
Nie z setTimeout, callbacków obietnic ani fetch().then(), które uruchamiasz sam. Łapią natomiast odrzuconą obietnicę odczytaną przez use, a w React 19 błąd rzucony wewnątrz akcji formularza albo funkcji przekazanej do startTransition z useTransition.
Jak zresetować granicę błędów?
Wyczyść jej stan błędu, zwykle przyciskiem Try again w widoku zastępczym, po naprawieniu tego, co spowodowało błąd. Zmiana key granicy też ją resetuje, bo React montuje nową instancję.