Error Boundary — это компонент React, который перехватывает JavaScript-ошибки в своём дочернем дереве компонентов и отображает запасной интерфейс вместо crash-страницы. В контексте React Native он предотвращает полный сбой приложения при нефатальных ошибках рендеринга. По данным React Documentation, 2024, error boundary ловит ошибки в методах render, lifecycle-хуках и конструкторах дочерних компонентов, позволяя приложению продолжить работу. В React Native это особенно критично, так как мобильное приложение не перезагружается по F5 — пользователь теряет сеанс целиком.
Главное
Error Boundary — это механизм React для graceful degradation при ошибках рендеринга. Введён в React 16 (2017) как компонент-обёртка, реализующий один из двух методов жизненного цикла: static getDerivedStateFromError или componentDidCatch. Error Boundary позволяет показать пользователю осмысленное сообщение вместо пустого белого экрана или полного краша приложения.
До React 16 любая необработанная ошибка в render приводила к crash всего приложения с размонтированием всего DOM-дерева. В web-приложениях это означало пустой белый экран, в React Native — полный crash приложения с возвратом на Home Screen. React-команда ввела Error Boundary как аналог catch-блока для декларативного UI, заимствовав концепцию из подхода «let it crash» в языке Erlang.
В React Native отсутствие Error Boundary означает полный crash приложения при любой ошибке рендеринга. Пользователь теряет весь текущий сеанс без возможности восстановления. Error Boundary в React Native критически важен, поскольку мобильные приложения не перезагружаются как веб-страницы — user session безвозвратно теряется, и пользователю приходится начинать всё заново.
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("Caught by 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 поддерживает эту функциональность только для классовых компонентов, поскольку требуется доступ к lifecycle-методам. В библиотеке react-error-boundary доступна готовая реализация с хуковым 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 хук, позволяя сбросить состояние без перезагрузки. Для типовых проектов схема с тремя уровнями Boundary считается оптимальной для React Native приложений.
После возникновения ошибки пользователь может нажать кнопку «Повторить», которая сбрасывает hasError в false и перерендеривает дочернее дерево. Reset-механизм важен для восстановления работоспособности приложения без перезагрузки. В react-error-boundary используется onReset callback, который может очищать кэш, перезапрашивать данные или обновлять состояние выше по дереву.
Error Boundary не ловит асинхронные ошибки — ошибки в setTimeout, setInterval, Promise, async/await. React не может перехватить ошибки вне render-цикла и lifecycle-хуков, так как они выполняются в других контекстах выполнения. Для асинхронных ошибок требуется отдельный try-catch в обработчиках или глобальный обработчик unhandledrejection event.
Ошибки в onClick, onChange и других event-обработчиках не перехватываются Error Boundary, потому что они выполняются вне React-рендеринга. Обработка ошибок для event-обработчиков должна быть внутри самого обработчика через try-catch. Библиотека react-error-boundary предоставляет хук useErrorHandler для проброса ошибок из event-обработчиков в ближайший Boundary.
Error Boundary не работает на серверной стороне в Next.js или Gatsby. Методы getDerivedStateFromError и componentDidCatch не вызываются при SSR, поскольку lifecycle-методы доступны только в браузере. Для серверных ошибок требуется отдельная стратегия: try-catch в getServerSideProps, fallback-страницы error.js (Next.js 13+) или глобальный middleware.
В React Native Error Boundary не предотвращает crash на уровне native-модулей. Native crash (segfault, out-of-memory, native exception) происходит на уровне Objective-C или Java и не достигает JavaScript-слоя. Для native-крашей требуется Crashlytics NDK (Android) или KSCrash (iOS). Error Boundary защищает только JavaScript-слой React Native приложения.
Размещайте Error Boundary на границах смысловых модулей: один Boundary на экран, один на виджет стороннего разработчика, один на сложную форму. Это изолирует ошибки и позволяет пользователю продолжать работу в остальных частях приложения. Корневой Boundary должен быть всегда — на случай критических ошибок в общих компонентах навигации или провайдерах. Каждый Boundary отвечает за свой фрагмент интерфейса и не влияет на соседние компоненты при возникновении ошибки.
Всегда передавайте ошибку в Crashlytics или Sentry через componentDidCatch. Добавляйте контекст: название экрана, userId, версию приложения, параметры навигации. В Sentry доступны breadcrumbs — последовательность действий пользователя до ошибки. Для анализа частоты non-fatal ошибок используйте dashboards Crashlytics с группировкой по issue.
Не используйте «голый» fallback — делайте осмысленный интерфейс. Рекомендуемый набор для React Native: сообщение об ошибке (user-friendly, не техническое), кнопка «Повторить», ссылка на поддержку или чат. Избегайте пустого View — пользователь подумает, что приложение сломалось полностью, и закроет его. Fallback должен быть встроен в общий дизайн приложения.
Тестируйте каждый Error Boundary с помощью React Testing Library или React Native Testing Library. Создайте компонент-триггер, который выбрасывает ошибку при рендере, и проверьте, что отображается fallback UI. Для интеграционных тестов используйте storybook с разными состояниями Error Boundary: нормальное, ошибочное, состояние после сброса. Автоматизированное тестирование Boundary гарантирует, что при изменении компонента fallback UI продолжает корректно работать в production. Покрытие тестами каждого Boundary должно быть обязательным требованием code review для React Native проектов.
Часто задаваемые вопросы
React реализует Error Boundary только через классовые компоненты, потому что требуется доступ к lifecycle-методам componentDidCatch и getDerivedStateFromError. Функциональные компоненты не имеют таких методов. Библиотека react-error-boundary предоставляет готовую классовую обёртку с хуковым API для удобства использования.
Влияние минимально — Error Boundary добавляет проверку состояния при каждом рендере дочернего дерева. Сравнение state.hasError — это O(1) операция с константной сложностью. При отсутствии ошибок overhead отсутствует. Только при возникновении ошибки Boundary выполняет дополнительный рендер fallback UI.
Нет, достаточно 2–3 уровней: корневой Boundary для всего приложения, экранный Boundary для каждой навигационной ветки и локальный Boundary для критических виджетов (платёжная форма, карта, чат). Чрезмерное количество Boundary усложняет архитектуру без значительной пользы.
Error Boundary и Suspense независимы: Suspense ловит загрузку (pending Promise в React 18+), Error Boundary ловит ошибки рендеринга. Их можно комбинировать:
try-catch ловит ошибки в синхронном императивном коде, но не может перехватить ошибки рендеринга JSX. Error Boundary специально спроектирован для декларативного UI: он перехватывает ошибки в render, lifecycle-хуках и конструкторах дочерних компонентов, что try-catch сделать не может из-за особенностей React-рендеринга.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также