Error Boundary — to komponent React, który przechwytuje błędy JavaScript w swoim drzewie komponentów potomnych i wyświetla interfejs zastępczy zamiast strony crasha. W kontekście React Native zapobiega całkowitemu zawieszeniu aplikacji przy niekrytycznych błędach renderowania. Według React Documentation, 2024, error boundary przechwytuje błędy w metodach render, lifecycle-hookach i konstruktorach komponentów potomnych, umożliwiając kontynuację działania aplikacji. W React Native jest to szczególnie krytyczne, ponieważ aplikacja mobilna nie przeładowuje się przez F5 — użytkownik traci całą sesję.
Najważniejsze
Error Boundary — to mechanizm React do graceful degradation przy błędach renderowania. Wprowadzony w React 16 (2017) jako komponent-opakowujący, implementujący jedną z dwóch metod cyklu życia: static getDerivedStateFromError lub componentDidCatch. Error Boundary pozwala wyświetlić użytkownikowi zrozumiały komunikat zamiast pustego białego ekranu lub całkowitego crasha aplikacji.
Przed React 16 każdy nieobsłużony błąd w render prowadził do crasha całej aplikacji z odmontowaniem całego drzewa DOM. W aplikacjach web oznaczało to pusty biały ekran, w React Native — całkowity crash aplikacji z powrotem do Home Screen. Zespół React wprowadził Error Boundary jako odpowiednik catch-bloku dla deklaratywnego UI, zapożyczając koncepcję z podejścia „let it crash” w języku Erlang.
W React Native brak Error Boundary oznacza całkowity crash aplikacji przy każdym błędzie renderowania. Użytkownik traci całą bieżącą sesję bez możliwości odzyskania. Error Boundary w React Native jest krytycznie ważny, ponieważ aplikacje mobilne nie przeładowują się jak strony internetowe — sesja użytkownika jest bezpowrotnie tracona i użytkownik musi zaczynać wszystko od nowa.
Error Boundary działa na poziomie drzewa React. Gdy komponent potomny wyrzuca błąd w render lub lifecycle, React nie odmontowuje całego drzewa, lecz przekazuje sterowanie do najbliższego Error Boundary wyżej w hierarchii. Boundary wywołuje getDerivedStateFromError, ustawia state.hasError = true i renderuje fallback UI zamiast uszkodzonej gałęzi komponentów.
import React, { Component, ErrorInfo, ReactNode } from "react"
interface Props {
children: ReactNode
fallback?: ReactNode
}
interface State {
hasError: boolean
error?: Error
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
componentDidCatch(error: Error, info: ErrorInfo) {
console.error("Przechwycone przez boundary:", error)
Crashlytics.recordException(error)
}
render() {
if (this.state.hasError) {
return this.props.fallback || <FallbackUI />
}
return this.props.children
}
}
getDerivedStateFromError ustawia stan do renderowania fallback UI — to metoda statyczna wywoływana na etapie renderowania przed zatwierdzeniem zmian. componentDidCatch wykonuje się na etapie zatwierdzania i jest przeznaczony do efektów ubocznych: logowania, wysyłania crash-reportów do Crashlytics, analityki. Dwie metody dzielą odpowiedzialność między zarządzaniem stanem UI a działaniami ubocznymi.
Aby utworzyć Error Boundary, należy zaimplementować komponent klasowy z metodami getDerivedStateFromError i/lub componentDidCatch. Komponenty funkcyjne nie mogą być Error Boundary — React wspiera tę funkcjonalność tylko dla komponentów klasowych, ponieważ wymagany jest dostęp do metod cyklu życia. W bibliotece react-error-boundary dostępna jest gotowa implementacja z API hooków dla wygody.
// Przykład użycia Error Boundary w React Native
import { ErrorBoundary } from "react-error-boundary"
const FallbackComponent = ({ error, resetError }: FallbackProps) => (
<View style={styles.container}>
<Text>Coś poszło nie tak</Text>
<Text>{error.message}</Text>
<Button title="Ponów" onPress={resetError} />
</View>
)
const App = () => (
<SafeAreaView>
<ErrorBoundary FallbackComponent={<FallbackComponent />}>
<UserProfile userId={"123"} />
</ErrorBoundary>
<BottomNavigation />
</SafeAreaView>
)
Poziomy opakowania — Error Boundary można umieszczać na różnych poziomach hierarchii. Jeden globalny Boundary na korzeniu aplikacji pokaże fallback UI przy każdym błędzie, ale nawigacja nadal będzie działać. Kilka Boundary na poziomie ekranów pozwala izolować błędy: jeśli jeden ekran się zepsuł, pozostałe działają niezależnie. react-error-boundary upraszcza mechanizm Reset przez hook useErrorBoundary, umożliwiając zresetowanie stanu bez przeładowania. Dla typowych projektów schemat z trzema poziomami Boundary jest uważany za optymalny dla aplikacji React Native.
Po wystąpieniu błędu użytkownik może nacisnąć przycisk „Ponów”, który resetuje hasError na false i ponownie renderuje drzewo potomne. Mechanizm Reset jest ważny dla przywrócenia działania aplikacji bez przeładowania. W react-error-boundary używany jest callback onReset, który może czyścić cache, ponownie pobierać dane lub aktualizować stan wyżej w drzewie.
Error Boundary nie przechwytuje błędów asynchronicznych — błędów w setTimeout, setInterval, Promise, async/await. React nie może przechwycić błędów poza cyklem render i lifecycle-hookami, ponieważ są one wykonywane w innych kontekstach wykonania. Dla błędów asynchronicznych wymagany jest osobny try-catch w handlerach lub globalny handler zdarzenia unhandledrejection.
Błędy w onClick, onChange i innych event-handlerach nie są przechwytywane przez Error Boundary, ponieważ są wykonywane poza renderowaniem React. Obsługa błędów dla event-handlerów powinna być wewnątrz samego handlera przez try-catch. Biblioteka react-error-boundary udostępnia hook useErrorHandler do przekazywania błędów z event-handlerów do najbliższego Boundary.
Error Boundary nie działa po stronie serwera w Next.js lub Gatsby. Metody getDerivedStateFromError i componentDidCatch nie są wywoływane przy SSR, ponieważ metody cyklu życia są dostępne tylko w przeglądarce. Dla błędów serwerowych wymagana jest osobna strategia: try-catch w getServerSideProps, strony fallbackowe error.js (Next.js 13+) lub globalny middleware.
W React Native Error Boundary nie zapobiega crashom na poziomie modułów natywnych. Crash natywny (segfault, out-of-memory, wyjątek natywny) występuje na poziomie Objective-C lub Java i nie dociera do warstwy JavaScript. Dla crashy natywnych wymagany jest Crashlytics NDK (Android) lub KSCrash (iOS). Error Boundary chroni tylko warstwę JavaScript aplikacji React Native.
Umieszczaj Error Boundary na granicach modułów znaczeniowych: jeden Boundary na ekran, jeden na widget zewnętrznego developera, jeden na złożony formularz. To izoluje błędy i pozwala użytkownikowi kontynuować pracę w pozostałych częściach aplikacji. Korzeniowy Boundary powinien być zawsze — na wypadek krytycznych błędów w wspólnych komponentach nawigacji lub providerach. Każdy Boundary odpowiada za swój fragment interfejsu i nie wpływa na sąsiednie komponenty przy wystąpieniu błędu.
Zawsze przekazuj błąd do Crashlytics lub Sentry przez componentDidCatch. Dodawaj kontekst: nazwę ekranu, userId, wersję aplikacji, parametry nawigacji. W Sentry dostępne są breadcrumbs — sekwencja działań użytkownika przed błędem. Do analizy częstotliwości błędów non-fatal używaj dashboardów Crashlytics z grupowaniem według issue.
Nie używaj „gołego” fallbacka — twórz znaczący interfejs. Zalecany zestaw dla React Native: komunikat o błędzie (user-friendly, nie techniczny), przycisk „Ponów”, link do pomocy lub czatu. Unikaj pustego View — użytkownik pomyśli, że aplikacja całkowicie się zepsuła i ją zamknie. Fallback powinien być wbudowany w ogólny design aplikacji.
Testuj każdy Error Boundary za pomocą React Testing Library lub React Native Testing Library. Stwórz komponent-trigger, który wyrzuca błąd przy renderze, i sprawdź, czy wyświetla się fallback UI. Do testów integracyjnych używaj storybook z różnymi stanami Error Boundary: normalnym, błędnym, stanem po resecie. Zautomatyzowane testowanie Boundary gwarantuje, że przy zmianie komponentu fallback UI nadal działa poprawnie w production. Pokrycie testami każdego Boundary powinno być obowiązkowym wymaganiem code review dla projektów React Native.
Często zadawane pytania
React implementuje Error Boundary tylko przez komponenty klasowe, ponieważ wymagany jest dostęp do metod cyklu życia componentDidCatch i getDerivedStateFromError. Komponenty funkcyjne nie mają takich metod. Biblioteka react-error-boundary udostępnia gotowe opakowanie klasowe z API hooków dla wygody użytkowania.
Wpływ jest minimalny — Error Boundary dodaje sprawdzenie stanu przy każdym renderze drzewa potomnego. Porównanie state.hasError to operacja O(1) o stałej złożoności. Przy braku błędów overhead nie występuje. Tylko przy wystąpieniu błędu Boundary wykonuje dodatkowy render fallback UI.
Nie, wystarczą 2–3 poziomy: korzeniowy Boundary dla całej aplikacji, ekranowy Boundary dla każdej gałęzi nawigacyjnej i lokalny Boundary dla krytycznych widgetów (formularz płatności, mapa, czat). Nadmierna liczba Boundary komplikuje architekturę bez znaczącej korzyści.
Error Boundary i Suspense są niezależne: Suspense przechwytuje ładowanie (pending Promise w React 18+), Error Boundary przechwytuje błędy renderowania. Można je łączyć: <ErrorBoundary><Suspense><Component /></Suspense></ErrorBoundary>. Suspense działa najpierw przy ładowaniu, Error Boundary — przy błędzie załadowanego komponentu.
try-catch przechwytuje błędy w synchronicznym kodzie imperatywnym, ale nie może przechwycić błędów renderowania JSX. Error Boundary jest specjalnie zaprojektowany dla deklaratywnego UI: przechwytuje błędy w render, lifecycle-hookach i konstruktorach komponentów potomnych, czego try-catch nie może zrobić ze względu na specyfikę renderowania React.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również