Error Boundaries

Error Boundary — React-компонент, который перехватывает JavaScript-ошибки в дочернем дереве и отображает fallback UI вместо краша всего приложения.

Зачем нужно

Без Error Boundaries любая необработанная ошибка в компоненте размонтирует всё дерево React и пользователь видит белый экран. Error Boundaries позволяют изолировать сбой: упавший виджет показывает сообщение об ошибке, остальная часть приложения продолжает работать. Это критически важно для производственных приложений — аналог try/catch для компонентного дерева.

Где используется

  • Оборачивание виджетов дашбордов, каждый из которых может упасть независимо
  • Защита маршрутов в react-router (каждый route за своим Error Boundary)
  • Оборачивание сторонних компонентов с непредсказуемым поведением
  • Разграничение критической и некритической части UI

Реализация Error Boundary

Error Boundary можно реализовать только через class-компонент (хуки не перехватывают ошибки рендеринга):

import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  // Вызывается при ошибке в дочернем дереве
  // Используется для обновления state и отображения fallback
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  // Вызывается после getDerivedStateFromError
  // Используется для логирования ошибки
  componentDidCatch(error, info) {
    console.error('Error Boundary поймал ошибку:', error);
    console.error('Стек компонентов:', info.componentStack);
    // Здесь можно отправить ошибку в Sentry, Datadog и т.д.
    // logErrorToService(error, info);
  }

  render {
    if (this.state.hasError) {
      // Кастомный fallback или переданный через пропс
      if (this.props.fallback) {
        return this.props.fallback;
      }
      return (
        <div role="alert" style={{ padding: '16px', color: 'red' }}>
          <h2>Что-то пошло не так</h2>
          <button onClick={ => this.setState({ hasError: false, error: null })}>
            Попробовать снова
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

// Использование
function App() {
  return (
    <div>
      <ErrorBoundary fallback={<p>Шапка недоступна</p>}>
        <Header />
      </ErrorBoundary>

      <ErrorBoundary fallback={<p>Контент временно недоступен</p>}>
        <MainContent />
      </ErrorBoundary>

      {/* Каждый виджет изолирован */}
      <ErrorBoundary fallback={<p>Виджет недоступен</p>}>
        <DataWidget />
      </ErrorBoundary>
    </div>
  );
}

Обёртка-компонент с react-error-boundary

Библиотека react-error-boundary упрощает использование:

import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>Ошибка: {error.message}</p>
      <button onClick={resetErrorBoundary}>Обновить</button>
    </div>
  );
}

function App() {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onError={(error, info) => logError(error, info)}
      onReset={ => { /* сбросить состояние приложения */ }}
    >
      <MyComponent />
    </ErrorBoundary>
  );
}

Частые ошибки

  • Error Boundaries не перехватывают асинхронные ошибки — ошибки в setTimeout, fetch-запросах или обработчиках событий нужно обрабатывать вручную через try/catch.
  • Один Error Boundary на всё приложение — при одной ошибке упадёт весь UI; оборачивайте отдельные секции.
  • Не логируют ошибки в componentDidCatch — ошибки должны отправляться в систему мониторинга (Sentry, Bugsnag).
  • Не предусматривают сброс — без кнопки «попробовать снова» пользователь вынужден перезагружать страницу.

Связанные темы

Ресурсы