Error boundaries и отслеживание ошибок

Понятие Error Boundaries

Error boundaries — это специальные компоненты, которые ловят ошибки JavaScript в дочерних компонентах, предотвращая полный краш приложения. Они не ловят ошибки внутри самих себя, асинхронные ошибки или ошибки в обработчиках событий. Основная цель — изолировать сбои и предоставить запасной UI, сохранив остальную часть интерфейса работоспособной.

В Preact, начиная с версии X, поддержка Error Boundaries реализована через метод жизненного цикла componentDidCatch и статический метод getDerivedStateFromError.

Создание Error Boundary

Для создания компонента, способного ловить ошибки, необходимо:

  1. Наследовать компонент от Component.
  2. Определить метод componentDidCatch(error, info).
  3. Использовать статический метод getDerivedStateFromError(error) для изменения состояния и отображения запасного интерфейса.

Пример базовой структуры:

import { h, Component } from 'preact';

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

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    console.error('Ошибка в дочернем компоненте:', error);
    console.error('Информация об ошибке:', info);
  }

  render() {
    if (this.state.hasError) {
      return <h2>Произошла ошибка. Попробуйте позже.</h2>;
    }

    return this.props.children;
  }
}

В этом примере getDerivedStateFromError переводит компонент в состояние ошибки, а componentDidCatch предоставляет возможность логирования или отправки данных об ошибке на сервер.

Ловля ошибок в асинхронных функциях и событиях

Error boundaries не отслеживают ошибки в:

  • Обработчиках событий.
  • Асинхронных вызовах (setTimeout, fetch).
  • Ошибках, происходящих вне дерева компонентов Preact.

Чтобы отлавливать такие ошибки, нужно использовать отдельные механизмы:

// Пример для асинхронного вызова
async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Ошибка при загрузке данных:', error);
  }
}

// Пример для события
function handleClick() {
  try {
    throw new Error('Симулированная ошибка события');
  } catch (error) {
    console.error(error);
  }
}

Комбинирование с Suspense

Preact поддерживает концепцию Suspense для ленивой загрузки компонентов. Error boundaries и Suspense работают вместе: Suspense обрабатывает состояние загрузки, а Error Boundary — сбои.

import { h, lazy, Suspense } from 'preact';

const LazyComponent = lazy(() => import('./LazyComponent'));

<ErrorBoundary>
  <Suspense fallback={<div>Загрузка...</div>}>
    <LazyComponent />
  </Suspense>
</ErrorBoundary>

Если ленивый компонент не сможет загрузиться или выбросит ошибку, Error Boundary перехватит её и покажет запасной интерфейс.

Логирование и аналитика ошибок

Одним из ключевых применений Error Boundaries является сбор данных об ошибках. В production-сценариях часто используют сервисы вроде Sentry или LogRocket. В методе componentDidCatch можно интегрировать вызов внешнего API:

componentDidCatch(error, info) {
  sendErrorToServer({ error, info, componentStack: info.componentStack });
}

Советы по применению

  • Размещать Error Boundaries на уровне крупных разделов приложения, а не для каждого мелкого компонента.
  • Создавать несколько слоев Error Boundaries: глобальный для всего приложения и локальные для критически важных секций.
  • Хранить минимальный запасной UI, чтобы пользователь видел работающее приложение даже при частичных сбоях.
  • Не использовать Error Boundaries для обычной обработки ошибок логики — они предназначены только для неожиданных исключений.

Ограничения

  • Не ловят синтаксические ошибки до рендеринга.
  • Не покрывают ошибки в хуках useEffect, если они вызывают асинхронные операции.
  • Не перехватывают ошибки в контекстных провайдерах или роутах, если ошибка происходит вне дерева дочерних компонентов Error Boundary.

Заключение по структуре

Error boundaries в Preact — мощный инструмент защиты интерфейса. Их корректное применение позволяет разделять ответственность за стабильность и логику, логировать ошибки для анализа и минимизировать влияние сбоев на пользовательский опыт. Оптимальная архитектура предполагает использование нескольких уровней границ ошибок и комбинацию с Suspense для управления загрузкой и ленивой подгрузкой компонентов.