Error Boundary е React компонент, който улавя JavaScript грешки в своето дъщерно дърво от компоненти и показва резервен интерфейс вместо страница за срив. В контекста на React Native предотвратява пълния срив на приложението при нефатални грешки при рендиране. Според React Documentation, 2024, error boundary улавя грешки в render методите, lifecycle-hooks и конструкторите на дъщерни компоненти, позволявайки на приложението да продължи работа. В React Native това е особено критично, тъй като мобилното приложение не се презарежда с F5 — потребителят губи цялата сесия.
Основни точки
Error Boundary е механизъм на React за graceful degradation при грешки при рендиране. Въведен в React 16 (2017) като компонент-обвивка, имплементиращ един от двата метода на жизнения цикъл: static getDerivedStateFromError или componentDidCatch. Error Boundary позволява на потребителя да види смислено съобщение вместо празен бял екран или пълен срив на приложението.
Преди React 16 всяка необработена грешка в render водеше до срив на цялото приложение с демонтиране на цялото DOM дърво. В уеб приложенията това означаваше празен бял екран, в React Native — пълен срив на приложението с връщане към Home Screen. Екипът на React въведе Error Boundary като аналог на catch-блок за декларативен UI, заимствайки концепцията от подхода „остави го да се срине” в езика Erlang.
В React Native липсата на Error Boundary означава пълен срив на приложението при всяка грешка при рендиране. Потребителят губи цялата текуща сесия без възможност за възстановяване. Error Boundary в React Native е критично важен, тъй като мобилните приложения не се презареждат като уеб страници — потребителската сесия се губи безвъзвратно и потребителят трябва да започне всичко отначало.
Error Boundary работи на ниво React дърво. Когато дъщерен компонент хвърли грешка в render или lifecycle, React не демонтира цялото дърво, а предава контрола на най-близкия Error Boundary по-нагоре в йерархията. Boundary извиква getDerivedStateFromError, задава state.hasError = true и рендира fallback UI вместо счупения клон от компоненти.
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("Уловено от boundary:", error)
Crashlytics.recordException(error)
}
render() {
if (this.state.hasError) {
return this.props.fallback || <FallbackUI />
}
return this.props.children
}
}
getDerivedStateFromError задава състоянието за рендиране на fallback UI — това е статичен метод, извикван във фазата на рендиране преди фиксиране на промените. componentDidCatch се изпълнява във фазата на фиксиране и е предназначен за странични ефекти: логване, изпращане на crash-репорти до Crashlytics, аналитика. Двата метода разделят отговорността между управлението на състоянието на UI и страничните действия.
За създаване на Error Boundary трябва да имплементирате класов компонент с методите getDerivedStateFromError и/или componentDidCatch. Функционалните компоненти не могат да бъдат Error Boundary — React поддържа тази функционалност само за класови компоненти, тъй като е необходим достъп до методите на жизнения цикъл. В библиотеката react-error-boundary е налична готова имплементация с hooks API за удобство.
// Пример за използване на Error Boundary в React Native
import { ErrorBoundary } from "react-error-boundary"
const FallbackComponent = ({ error, resetError }: FallbackProps) => (
<View style={styles.container}>
<Text>Нещо се обърка</Text>
<Text>{error.message}</Text>
<Button title="Опитай отново" onPress={resetError} />
</View>
)
const App = () => (
<SafeAreaView>
<ErrorBoundary FallbackComponent={<FallbackComponent />}>
<UserProfile userId={"123"} />
</ErrorBoundary>
<BottomNavigation />
</SafeAreaView>
)
Нива на обвивка — Error Boundary може да бъде поставен на различни нива в йерархията. Един глобален Boundary в корена на приложението ще покаже fallback UI при всяка грешка, но навигацията ще продължи да работи. Няколко Boundary на ниво екрани позволяват изолиране на грешки: ако един екран се счупи, останалите продължават да работят независимо. react-error-boundary опростява механизма Reset чрез useErrorBoundary hook, позволявайки нулиране на състоянието без презареждане. За типични проекти схемата с три нива на Boundary се счита за оптимална за React Native приложения.
След възникване на грешка потребителят може да натисне бутона „Опитай отново”, който нулира hasError на false и прендира отново дъщерното дърво. Механизмът Reset е важен за възстановяване на работоспособността на приложението без презареждане. В react-error-boundary се използва onReset callback, който може да изчисти кеша, да поиска отново данни или да актуализира състоянието по-нагоре в дървото.
Error Boundary не улавя асинхронни грешки — грешки в setTimeout, setInterval, Promise, async/await. React не може да улови грешки извън цикъла на рендиране и lifecycle-hooks, тъй като те се изпълняват в други контексти на изпълнение. За асинхронни грешки е необходим отделен try-catch в обработчиците или глобален обработчик на събитието unhandledrejection.
Грешките в onClick, onChange и други event-обработчици не се улавят от Error Boundary, тъй като те се изпълняват извън React рендирането. Обработка на грешки за event-обработчици трябва да бъде вътре в самия обработчик чрез try-catch. Библиотеката react-error-boundary предоставя hook useErrorHandler за прехвърляне на грешки от event-обработчици към най-близкия Boundary.
Error Boundary не работи от сървърна страна в Next.js или Gatsby. Методите getDerivedStateFromError и componentDidCatch не се извикват при SSR, тъй като методите на жизнения цикъл са достъпни само в браузъра. За сървърни грешки е необходима отделна стратегия: try-catch в getServerSideProps, fallback страници error.js (Next.js 13+) или глобален middleware.
В React Native Error Boundary не предотвратява срив на ниво native модули. Native crash (segfault, out-of-memory, native изключение) се случва на ниво Objective-C или Java и не достига до JavaScript слоя. За native сривове е необходим Crashlytics NDK (Android) или KSCrash (iOS). Error Boundary защитава само JavaScript слоя на React Native приложението.
Поставете Error Boundary на границите на смислови модули: един Boundary на екран, един на widget на външен разработчик, един на сложна форма. Това изолира грешките и позволява на потребителя да продължи работа в останалите части на приложението. Коренният Boundary трябва винаги да съществува — за критични грешки в общите навигационни компоненти или provider-и. Всеки Boundary отговаря за своя фрагмент от интерфейса и не влияе на съседните компоненти при възникване на грешка.
Винаги предавайте грешката на Crashlytics или Sentry чрез componentDidCatch. Добавяйте контекст: име на екрана, userId, версия на приложението, параметри за навигация. В Sentry са налични breadcrumbs — последователност от действия на потребителя преди грешката. За анализ на честотата на non-fatal грешки използвайте Crashlytics dashboard-и с групиране по issue.
Не използвайте „гол” fallback — създайте смислен интерфейс. Препоръчителен комплект за React Native: съобщение за грешка (приятелско към потребителя, не техническо), бутон „Опитай отново”, връзка за поддръжка или чат. Избягвайте празен View — потребителят ще помисли, че приложението се е сринало напълно, и ще го затвори. Fallback трябва да бъде вграден в общия дизайн на приложението.
Тествайте всеки Error Boundary с React Testing Library или React Native Testing Library. Създайте trigger компонент, който хвърля грешка при рендиране, и проверете дали fallback UI се показва. За интеграционни тестове използвайте storybook с различни състояния на Error Boundary: нормално, грешка, състояние след нулиране. Автоматизираното тестване на Boundary гарантира, че при промяна на компонента fallback UI продължава да работи правилно в production. Покритието с тестове на всеки Boundary трябва да бъде задължително изискване за code review на React Native проекти.
Често задавани въпроси
React имплементира Error Boundary само чрез класови компоненти, защото е необходим достъп до методите на жизнения цикъл componentDidCatch и getDerivedStateFromError. Функционалните компоненти нямат такива методи. Библиотеката react-error-boundary предоставя готова класова обвивка с hooks API за удобство при използване.
Влиянието е минимално — Error Boundary добавя проверка на състоянието при всяко рендиране на дъщерното дърво. Сравнението на state.hasError е O(1) операция с константна сложност. При липса на грешки overhead липсва. Само при възникване на грешка Boundary изпълнява допълнително рендиране на fallback UI.
Не, достатъчни са 2–3 нива: коренен Boundary за цялото приложение, екранен Boundary за всеки навигационен клон и локален Boundary за критични widget-и (платежна форма, карта, чат). Прекомерният брой Boundary усложнява архитектурата без значителна полза.
Error Boundary и Suspense са независими: Suspense улавя зареждане (pending Promise в React 18+), Error Boundary улавя грешки при рендиране. Те могат да се комбинират: <ErrorBoundary><Suspense><Component /></Suspense></ErrorBoundary>. Suspense се изпълнява първи при зареждане, Error Boundary — при грешка на заредения компонент.
try-catch улавя грешки в синхронен императивен код, но не може да улови грешки при рендиране на JSX. Error Boundary е специално проектиран за декларативен UI: той улавя грешки в render, lifecycle-hooks и конструкторите на дъщерни компоненти, което try-catch не може да направи поради особеностите на React рендирането.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също