Обработка ошибок

Haunted — это библиотека для создания веб-компонентов с использованием функционального подхода и реактивного состояния в стиле Hooks. Одной из важных задач при разработке компонентов является грамотная обработка ошибок, возникающих как в процессе рендеринга, так и при взаимодействии с асинхронными данными. В Haunted нет встроенной системы обработки ошибок уровня фреймворка, как в React с ErrorBoundary, поэтому требуется понимание того, как интегрировать обработку ошибок на уровне хуков и компонентов.

Перехват ошибок при рендеринге

В Haunted рендеринг компонентов основан на функции, возвращающей шаблон HTML через html из библиотеки lit-html. Любые синтаксические ошибки или исключения в логике функции-компонента приведут к остановке рендеринга. Для безопасного рендеринга можно использовать конструкцию try/catch внутри компонента:

import { html } from 'haunted';

function SafeComponent({ data }) {
  try {
    if (!data) throw new Error('Данные не определены');
    return html`<p>Значение: ${data}</p>`;
  } catch (err) {
    console.error('Ошибка в SafeComponent:', err);
    return html`<p>Произошла ошибка при отображении данных.</p>`;
  }
}

Особенности подхода:

  • Ошибка перехватывается локально в компоненте.
  • Рендеринг не ломает остальную часть DOM.
  • Можно выводить пользовательский интерфейс ошибки.

Асинхронные операции и ошибки

Компоненты Haunted часто используют асинхронные операции для получения данных. Для таких случаев обработка ошибок должна выполняться внутри асинхронных функций или хуков состояния (useState, useEffect). Пример использования асинхронного хука с обработкой ошибок:

import { html } from 'haunted';
import { useState, useEffect } from 'haunted';

function AsyncComponent({ url }) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    let isMounted = true;

    async function fetchData() {
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error(`Ошибка HTTP: ${response.status}`);
        const result = await response.json();
        if (isMounted) setData(result);
      } catch (err) {
        if (isMounted) setError(err);
      }
    }

    fetchData();

    return () => { isMounted = false; };
  }, [url]);

  if (error) return html`<p>Ошибка загрузки: ${error.message}</p>`;
  if (!data) return html`<p>Загрузка...</p>`;
  return html`<p>Данные получены: ${JSON.stringify(data)}</p>`;
}

Ключевые моменты:

  • Использование try/catch внутри асинхронной функции для перехвата ошибок.
  • Состояние error хранит информацию о проблеме, позволяя рендерить fallback UI.
  • Флаг isMounted предотвращает попытку изменения состояния после размонтирования компонента.

Централизованная обработка ошибок

Для крупных приложений удобно создавать обёртки, выполняющие функции «Error Boundary». В Haunted это можно реализовать через компонент высшего порядка (HOC) или специальный хук. Пример HOC:

import { html } from 'haunted';

function withErrorBoundary(Component) {
  return function ErrorBoundaryWrapper(props) {
    try {
      return Component(props);
    } catch (err) {
      console.error('Ошибка в компоненте:', err);
      return html`<p>Произошла ошибка в приложении.</p>`;
    }
  };
}

const SafeAsyncComponent = withErrorBoundary(AsyncComponent);

Особенности HOC:

  • Позволяет повторно использовать логику обработки ошибок.
  • Перехватывает ошибки синхронного рендеринга.
  • Для асинхронных операций дополнительно требуется управление состоянием ошибок внутри компонента.

Обработка ошибок событий

Ошибки могут возникать в обработчиках событий, таких как click, input и других. В Haunted события добавляются через стандартные слушатели DOM, поэтому обработка ошибок сводится к привычным конструкциям try/catch:

function ButtonComponent() {
  function handleClick() {
    try {
      // Возможная проблемная логика
      throw new Error('Ошибка при нажатии');
    } catch (err) {
      console.error('Ошибка события:', err);
      alert('Произошла ошибка при обработке действия');
    }
  }

  return html`<button @click=${handleClick}>Нажми меня</button>`;
}

Особенности:

  • Ошибки не прерывают работу компонента полностью.
  • Позволяет информировать пользователя о проблеме сразу.

Логирование и интеграция с внешними сервисами

Для промышленных приложений важно не только отображать ошибки, но и собирать их для анализа. Любые catch блоки можно интегрировать с внешними сервисами:

catch (err) {
  console.error(err);
  sendErrorToMonitoringService(err);
}

Где sendErrorToMonitoringService — функция отправки данных в систему мониторинга (Sentry, LogRocket и т. д.).

Рекомендации по структуре обработки ошибок

  1. Локальные try/catch для критичных операций внутри компонента.
  2. Состояние ошибок (useState) для асинхронных действий.
  3. Обёртки и HOC для повторного использования логики и защиты компонентов.
  4. Обработка ошибок событий для пользовательских действий.
  5. Централизованное логирование для отслеживания и анализа проблем.

Такой подход обеспечивает надежность компонентов Haunted и предотвращает поломку интерфейса при возникновении исключений.