Обработка ошибок в React с помощью Error Boundaries

Быстрые ссылки
- Создание границ ошибок
- Какой метод использовать
- Где размещать границы ошибок
- Ограничения границ ошибок
- Практические рекомендации и сценарии
- Заключение
Коротко о концепции
Граница ошибок — это компонент, который «обрамляет» часть дерева компонентов и перехватывает JavaScript-ошибки, возникающие глубже по дереву. После перехвата можно отобразить запасной интерфейс (fallback UI) и сохранить видимыми остальные части приложения. Это не замена логированию или мониторингу — скорее средство локализации сбоев.
Важно: термин «граница ошибок» в статье далее будет упоминаться как Error Boundary для ясности.
Создание границ ошибок
Любой класс-компонент React может стать границей ошибок, если реализовать один или оба жизненных метода:
- componentDidCatch(error, info) — экземплярный метод; вызывается после того, как компонент поймал ошибку. Хорош для отправки данных в систему мониторинга.
- static getDerivedStateFromError(error) — статический метод; используется для обновления состояния и переключения на запасной UI.
Пример простой границы ошибок (с пояснениями):
class ErrorBoundary extends React.Component {
state = { error: null };
static getDerivedStateFromError(error) {
// Обновляем состояние, чтобы отобразить запасной UI при следующем рендере
return { error };
}
componentDidCatch(error, info) {
// Можно отправить подробности ошибки в сервис мониторинга
// logErrorToService(error, info);
}
render() {
if (this.state.error) {
return Произошла ошибка.
;
}
return this.props.children;
}
}Пояснения:
- getDerivedStateFromError вызывается на фазе рендера и должен вернуть объект состояния.
- componentDidCatch вызывается на фазе commit (после обновления DOM) и полезен для асинхронного логирования.
Пример использования в приложении
export const AppRoot = () => (
);В этом примере отдельная граница вокруг Router защищает навигацию и футер от сбоев в основном контенте.
Какой метод использовать
Оба метода можно определить вместе или по отдельности. Разница — в фазе, на которой они срабатывают:
- getDerivedStateFromError вызывается во время рендера, до обновления DOM. Это безопаснее для немедленной замены UI.
- componentDidCatch вызывается после обновления DOM (фаза commit) и подходит для логирования/побочного эффекта.
Практическое правило:
- используйте getDerivedStateFromError для переключения на запасной интерфейс;
- используйте componentDidCatch для отправки данных в систему мониторинга.
Важно: componentDidCatch — экземплярный метод, значит он может напрямую вызывать пропсы или методы экземпляра. getDerivedStateFromError статический и возвращает изменения состояния.
Где размещать границы ошибок
Рекомендуется иметь несколько границ ошибок, одну на каждом важном слое интерфейса:
- глобальная граница вокруг всего приложения (запасной экран на весь экран);
- границы на уровне маршрутов (чтобы падение контента не ломало шапку/навигацию);
- границы вокруг сложных виджетов (карты, редакторы, интеграции);
- при необходимости — мелкие границы вокруг потенциально ненадёжных сторонних компонентов.
Модель расположения:
- Изолируйте то, что можно восстановить локально (например, секция контента).
- Не стараетесь оборачивать весь код одной монолитной границей — это нивелирует пользу.
Пример иерархии:
// защищает основной контент
Если внутри MainContent есть потенциально хрупкая часть, добавьте вложенную границу.
Ограничения границ ошибок
Границы ошибок полезны, но имеют важные ограничения:
- Они не перехватывают ошибки в обработчиках событий. Обработчик событие не влияет на цикл рендера, поэтому для них нужны try/catch.
- Они не ловят ошибки асинхронного кода (например, setTimeout, промисы) — используйте try/catch в async/await и .catch для промисов.
- Граница ловит только ошибки в компонентах-потомках; ошибки самой границы не будут перехвачены этой же границей.
- Только класс-компоненты могут быть Error Boundary (на момент написания); функциональные компоненты напрямую не поддерживают роль границы.
Примеры, которые не будут пойманы:
// Исключение в обработчике не будет пойман границей — используйте:
try { /* ... */ } catch (e) { /* ... */ }Или асинхронный пример:
async function fetchData() {
const res = await fetch('/api');
// ошибки здесь нужно ловить .catch или try/catch вокруг await
}Важно: если хотите, чтобы асинхронная ошибка переводила интерфейс в запасной режим — поймайте её и обновите состояние компонента, который отображает UI.
Практическое руководство и готовые приёмы
Ниже набор практических шаблонов и чеклистов, которые пригодятся при внедрении границ ошибок в проект.
Шпаргалка: какие ошибки ловятся
- Ловятся: ошибки, брошенные во время рендера, в методах жизненного цикла и в конструкторах потомков.
- Не ловятся: ошибки в обработчиках событий, в асинхронных колбэках, в сервисных хелперах вне рендера.
Альтернативные подходы
- Использовать библиотеку типа react-error-boundary (она даёт удобный HOC/Hook-API, но под капотом остаётся класс-компонент).
- Дополнительно применять глобальное перехватывание ошибок window.onerror и window.onunhandledrejection для логирования и аналитики.
Роль-ориентированные чеклисты
Для разработчика:
- Реализовать ErrorBoundary с getDerivedStateFromError и componentDidCatch.
- Отобразить стилизованный запасной UI с элементами навигации или восстановлением.
- Логировать ошибки с контекстом (props, userId, route).
Для QA:
- Проверить, что при ошибке в компоненте запасной UI отображается корректно.
- Убедиться, что не ломается глобальный навбар/футер.
- Тестировать сценарии асинхронных ошибок и ошибок в обработчиках.
Для менеджера продукта / инженера:
- Определить критичные зоны приложения для размещения границ.
- Настроить метрики ошибок и оповещения из componentDidCatch.
Мини-методология быстрого реагирования (playbook)
- Локализовать: какая граница поймала ошибку? Собрать stack и context.
- Временно откатить релиз/фичу, если ошибка критична.
- Восстановить UI: позволить пользователям перейти к другим частям приложения.
- Подготовить исправление и тесты, добавить больше границ при необходимости.
Чек-лист для дизайна запасного интерфейса
- Показывать понятное сообщение (не только «Ошибка»).
- Дать путь восстановления: «Обновить», «Назад», «Вернуться на главную».
- Сохранить элементы навигации, если это безопасно.
Ментальные модели и эвристики
- Модель «капсулируй отказ»: думай о каждой крупной секции как о контейнере, который может аварийно завершиться; цель — не дать отказу распространиться.
- Эвристика «минимально вредно»: запасной UI должен позволять пользователю выполнить основные задачи (или перейти в безопасное место).
- Правило «лог — не замена UI»: логирование помогает разработчикам, запасной UI — пользователям.
Решение: когда граница бессильна
- Если компонент делает побочные эффекты вне рендера (например, напрямую пишет в DOM) и они ломаются — граница может не помочь.
- Если приложение зависло или браузер выдал критический crash — границы бессильны.
Сниппеты и шаблоны
Быстрая функция для логирования из componentDidCatch:
function logErrorToService(error, info) {
// Небольшая обёртка для отправки ошибок
fetch('/log', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ error: error.toString(), info })
}).catch(() => {/* не мешаем пользователю */});
}Пример запасного интерфейса с возможностью восстановления:
render() {
if (this.state.error) {
return (
Что-то пошло не так
Мы уже работаем над этим. Попробуйте вернуться на главную.
);
}
return this.props.children;
}Диагностическое дерево принятия решений
flowchart TD
A[Произошла ошибка] --> B{Где ошибка?}
B -->|В обработчике события| C[Использовать try/catch в обработчике]
B -->|Асинхронный код| D[Использовать try/catch или .catch для промиса]
B -->|Во время рендера или в lifecycle потомка| E[Ошибка может быть поймана Error Boundary]
E --> F{Есть ли граница вокруг компонента?}
F -->|Да| G[Показать запасной UI + логирование]
F -->|Нет| H[Ошибка всплывёт и вероятно сломает страницу]Отладка и тесты
Тесты приёмки должны покрывать:
- Рендер запасного UI при искусственно брошенном исключении в потомке.
- Сохранение рабочего состояния соседних компонентов (header/footer).
- Корректную отправку данных в логи/мониторинг.
Критерии приёмки
- При ошибке в компоненте отображается понятный запасной интерфейс.
- Ошибка логируется с контекстом.
- Ошибка одного маршрута не отключает навигацию и футер.
Частые ошибки внедрения и как их избежать
- Обрезать stack trace при логировании — включайте contextual props и current route.
- Делать один глобальный ErrorBoundary для всего — лучше иметь несколько гранулярных.
- Не тестировать асинхронные сценарии — добавьте unit/e2e для промисов и setTimeout.
Короткий глоссарий
- Error Boundary — класс-компонент, который перехватывает ошибки в потомках.
- getDerivedStateFromError — статический метод для переключения состояния на запасной UI.
- componentDidCatch — метод для побочных эффектов (логирование).
FAQ
Перехватят ли Error Boundaries ошибки в Redux-сайд-эффектах?
Нет: если сайд-эффект выполняется вне фазы рендера (например, в саге или thunk), такие ошибки нужно обрабатывать в самих сайд-эффектах и/или глобальным обработчиком.
Можно ли использовать функциональные компоненты как Error Boundary?
Прямо — нет. На момент написания Error Boundaries реализуются через класс-компоненты. Есть внешние библиотеки, которые дают hook-подобный API, но под капотом всё равно используется класс.
Что показывать пользователю в запасном UI?
Короткое объяснение, возможные действия (обновить, вернуться) и кнопка для перехода в безопасную часть приложения. Не показывайте технические детали ошибки пользователю.
Заключение
Error Boundaries — простой и мощный инструмент для повышения устойчивости интерфейса. Они помогают локализовать сбои, улучшить пользовательский опыт и дают место для сбора диагностики. Проектируйте границы по слоям, логируйте ошибки и не забывайте о сценариях, которые границы не покрывают (обработчики событий и асинхронный код).
Краткие рекомендации для старта:
- Добавьте глобальную и маршрутные границы в каждое приложение;
- Используйте getDerivedStateFromError для немедленного переключения UI;
- Отправляйте логи из componentDidCatch;
- Тестируйте как синхронные, так и асинхронные ошибки.
–
Социальная превью-подсказка:
- Заголовок: «Обработка ошибок в React: Error Boundaries»
- Описание: «Как и где ставить границы ошибок, чтобы изолировать сбои и дать пользователю понятный запасной интерфейс.»
Похожие материалы
Несколько аккаунтов Skype: Multi Skype Launcher
Журнал для работы: повысить продуктивность
Персональные звуки уведомлений на Android
Скачивание шоу Hulu для офлайн‑просмотра
Microsoft Start: персонализированная новостная лента