Error Boundary — це компонент React, який перехоплює JavaScript-помилки в своєму дочірньому дереві компонентів і відображає запасний інтерфейс замість сторінки аварійного завершення. У контексті React Native він запобігає повному збою застосунку при нефатальних помилках рендерингу. Згідно з React Documentation, 2024, error boundary ловить помилки в методах render, хуках життєвого циклу та конструкторах дочірніх компонентів, дозволяючи застосунку продовжити роботу. У React Native це особливо критично, оскільки мобільний застосунок не перезавантажується через F5 — користувач втрачає сеанс цілком.
Головне
Error Boundary — це механізм React для поступової деградації при помилках рендерингу. Введений у React 16 (2017) як компонент-обгортка, що реалізує один із двох методів життєвого циклу: static getDerivedStateFromError або componentDidCatch. Error Boundary дозволяє показувати користувачеві осмислене повідомлення замість пустого білого екрана або повного збою застосунку.
До React 16 будь-яка необроблена помилка в render призводила до повного збою застосунку з демонтажем усього DOM-дерева. У веб-застосунках це означало пустий білий екран, у React Native — повний збій із поверненням на головний екран. Команда React ввела Error Boundary як аналог catch-блоку для декларативного UI, запозичивши концепцію з підходу “let it crash” у мові Erlang.
У React Native відсутність Error Boundary означає повний збій застосунку при будь-якій помилці рендерингу. Користувач втрачає весь поточний сеанс без можливості відновлення. Error Boundary у React Native критично важливий, оскільки мобільні застосунки не перезавантажуються як веб-сторінки — сеанс користувача безповоротно втрачається, і користувачеві доводиться починати все заново.
Error Boundary працює на рівні React-дерева. Коли дочірній компонент викидає помилку в render або життєвому циклі, React не демонтує все дерево, а передає керування найближчому Error Boundary вище в ієрархії. Boundary викликає getDerivedStateFromError, встановлює state.hasError = true і рендерить запасний інтерфейс замість зламаної гілки компонентів.
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 встановлює стан для рендерингу запасного інтерфейсу — це статичний метод, що викликається на етапі рендерингу до фіксації змін. componentDidCatch виконується на етапі фіксації та призначений для побічних ефектів: логування, надсилання звітів про збій до Crashlytics, аналітики. Два методи розділяють відповідальність між керуванням станом UI та побічними діями.
Для створення Error Boundary потрібно реалізувати класовий компонент із методами getDerivedStateFromError та/або componentDidCatch. Функціональні компоненти не можуть бути Error Boundary — React підтримує цю функціональність тільки для класових компонентів, оскільки потрібен доступ до методів життєвого циклу. Бібліотека 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 на корені застосунку покаже запасний інтерфейс при будь-якій помилці, але навігація все одно збережеться. Кілька Boundary на рівні екранів дозволяють ізолювати помилки: якщо один екран зламався, інші продовжують працювати незалежно. react-error-boundary спрощує механізм скидання через хук useErrorBoundary, дозволяючи скинути стан без перезавантаження. Для типових проектів схема з трьома рівнями Boundary вважається оптимальною для React Native застосунків.
Після виникнення помилки користувач може натиснути кнопку “Повторити”, яка скидає hasError у false і перерендерює дочірнє дерево. Механізм скидання важливий для відновлення працездатності застосунку без перезавантаження. У react-error-boundary використовується onReset callback, який може очищати кеш, перезапитувати дані або оновлювати стан вище по дереву.
Error Boundary не ловить асинхронні помилки — помилки в setTimeout, setInterval, Promise, async/await. React не може перехопити помилки поза циклом рендерингу та хуками життєвого циклу, оскільки вони виконуються в інших контекстах виконання. Для асинхронних помилок потрібен окремий try-catch в обробниках або глобальний обробник події unhandledrejection.
Помилки в onClick, onChange та інших обробниках подій не перехоплюються Error Boundary, оскільки вони виконуються поза React-рендерингом. Обробка помилок для обробників подій має бути всередині самого обробника через try-catch. Бібліотека react-error-boundary надає хук useErrorHandler для пробросу помилок з обробників подій у найближчий Boundary.
Error Boundary не працює на серверній стороні в Next.js або Gatsby. Методи getDerivedStateFromError і componentDidCatch не викликаються при SSR, оскільки методи життєвого циклу доступні тільки в браузері. Для серверних помилок потрібна окрема стратегія: try-catch у getServerSideProps, запасні сторінки error.js (Next.js 13+) або глобальний middleware.
У React Native Error Boundary не запобігає збоям на рівні нативних модулів. Нативний збій (segfault, out-of-memory, нативний виняток) відбувається на рівні Objective-C або Java та не досягає JavaScript-шару. Для нативних збоїв потрібен Crashlytics NDK (Android) або KSCrash (iOS). Error Boundary захищає тільки JavaScript-шар React Native застосунку.
Розміщуйте Error Boundary на межах смислових модулів: один Boundary на екран, один на віджет стороннього розробника, один на складну форму. Це ізолює помилки та дозволяє користувачеві продовжувати роботу в інших частинах застосунку. Кореневий Boundary має бути завжди — на випадок критичних помилок у спільних компонентах навігації або провайдерах. Кожен Boundary відповідає за свій фрагмент інтерфейсу та не впливає на сусідні компоненти при виникненні помилки.
Завжди передавайте помилку в Crashlytics або Sentry через componentDidCatch. Додавайте контекст: назву екрана, userId, версію застосунку, параметри навігації. У Sentry доступні breadcrumbs — послідовність дій користувача до помилки. Для аналізу частоти non-fatal помилок використовуйте дашборди Crashlytics із групуванням по issue.
Не використовуйте “голий” запасний інтерфейс — робіть осмислений інтерфейс. Рекомендований набір для React Native: повідомлення про помилку (user-friendly, не технічне), кнопка “Повторити”, посилання на підтримку або чат. Уникайте пустого View — користувач подумає, що застосунок зламався повністю, і закриє його. Запасний інтерфейс має бути вбудований у загальний дизайн застосунку.
Тестуйте кожен Error Boundary за допомогою React Testing Library або React Native Testing Library. Створіть компонент-тригер, який викидає помилку при рендерингу, і перевірте, що відображається запасний інтерфейс. Для інтеграційних тестів використовуйте storybook з різними станами Error Boundary: нормальний, помилковий, стан після скидання. Автоматизоване тестування Boundary гарантує, що при зміні компонента запасний інтерфейс продовжує коректно працювати в production. Покриття тестами кожного Boundary має бути обов’язковою вимогою code review для React Native проектів.
Часті запитання
React реалізує Error Boundary тільки через класові компоненти, оскільки потрібен доступ до методів життєвого циклу componentDidCatch і getDerivedStateFromError. Функціональні компоненти не мають таких методів. Бібліотека react-error-boundary надає готову класову обгортку з хуковим API для зручності використання.
Вплив мінімальний — Error Boundary додає перевірку стану при кожному рендері дочірнього дерева. Порівняння state.hasError — це O(1) операція з константною складністю. При відсутності помилок навантаження відсутнє. Тільки при виникненні помилки Boundary виконує додатковий рендер запасного інтерфейсу.
Ні, достатньо 2–3 рівнів: кореневий Boundary для всього застосунку, екранний Boundary для кожної навігаційної гілки та локальний Boundary для критичних віджетів (платіжна форма, карта, чат). Надмірна кількість 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, хуках життєвого циклу та конструкторах дочірніх компонентів, що try-catch зробити не може через особливості React-рендерингу.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також