Files
router-lists-ui/QUICK_START_ERROR_HANDLING.md
T

11 KiB
Raw Blame History

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

5-минутное руководство по использованию новой системы обработки ошибок


🎯 Основные сценарии

1️⃣ Простая загрузка данных

import { useErrorHandler } from '../hooks/useErrorHandler';
import api from '../lib/api';

function MyComponent() {
  const { withErrorHandler } = useErrorHandler();

  const loadData = withErrorHandler(
    async () => {
      const response = await api.get('/domains-new');
      setData(response.data);
    },
    { successMessage: 'Данные загружены' }
  );

  return <button onClick={loadData}>Загрузить</button>;
}

Что происходит:

  • Автоматический retry при сбоях (до 3 раз)
  • User-friendly сообщения об ошибках
  • Успешное уведомление при загрузке
  • Нет дубликатов уведомлений

2️⃣ Кнопка с retry

import RetryButton from '../components/RetryButton';
import api from '../lib/api';

function MyComponent() {
  const [loading, setLoading] = useState(false);

  const saveData = async () => {
    setLoading(true);
    try {
      await api.post('/domains-new', { domains: [...] });
    } finally {
      setLoading(false);
    }
  };

  return (
    <RetryButton 
      onRetry={saveData} 
      loading={loading}
    >
      Сохранить
    </RetryButton>
  );
}

Что происходит:

  • Показывает индикатор загрузки
  • Автоматический retry при ошибках
  • Блокирует повторные клики

3️⃣ Ручная обработка ошибок

import { useErrorHandler } from '../hooks/useErrorHandler';
import api from '../lib/api';

function MyComponent() {
  const { handleError, handleSuccess } = useErrorHandler();

  const deleteItem = async (id) => {
    try {
      await api.delete(`/domains-new/${id}`);
      handleSuccess('Домен удалён');
    } catch (error) {
      handleError(error, { 
        component: 'MyComponent',
        action: 'delete',
        itemId: id 
      });
    }
  };

  return <button onClick={() => deleteItem(123)}>Удалить</button>;
}

Что происходит:

  • Полный контроль над обработкой
  • Контекст для отладки
  • User-friendly сообщения

🔧 Настройка (уже сделано!)

Все компоненты уже интегрированы в App.jsx:

<ErrorBoundary>           {/* Ловит ошибки React */}
  <Router>
    <QueryClientProvider>
      <NotifyProvider>
        <NetworkErrorHandler />  {/* Мониторит сеть */}
        <MainLayout />
      </NotifyProvider>
    </QueryClientProvider>
  </Router>
</ErrorBoundary>

Ничего дополнительно настраивать не нужно! 🎉


📦 Что уже работает автоматически

Автоматический Retry

Метод Ошибка Попытки
GET Network 3
GET Timeout 2
GET Server (5xx) 2
POST/PUT Network 1
POST/PUT Timeout 1

User-Friendly сообщения

Технические ошибкиПонятные сообщения

  • ECONNABORTED → "Сервер не отвечает"
  • 500 Internal Server Error → "Ошибка сервера. Попробуйте позже"
  • 400 Bad Request → "Ошибка валидации. Проверьте данные"
  • 404 Not Found → "Запрашиваемый ресурс не найден"

Offline Detection

  • Красный баннер при потере связи
  • Зелёный баннер при восстановлении
  • Автоматическое скрытие через 3 секунды

Error Boundary

  • Ловит ошибки рендеринга React
  • Показывает красивую страницу ошибки
  • Кнопки восстановления

🎨 Типы уведомлений

const { handleSuccess, handleError, handleWarning, handleInfo } = useErrorHandler();

handleSuccess('Данные сохранены');              // 🟢 Зелёное
handleError('Не удалось загрузить');            // 🔴 Красное
handleWarning('Изменения не сохранены');        // 🟡 Жёлтое
handleInfo('Проверьте обновления');             // 🔵 Синее

🚀 Продвинутые примеры

React Query интеграция

import { useQuery } from '@tanstack/react-query';
import { useErrorHandler } from '../hooks/useErrorHandler';
import api from '../lib/api';

function MyComponent() {
  const { handleError } = useErrorHandler();

  const { data, isLoading, refetch } = useQuery({
    queryKey: ['domains'],
    queryFn: async () => {
      const res = await api.get('/domains-new');
      return res.data;
    },
    onError: (error) => {
      handleError(error, { query: 'domains' });
    },
    retry: 3  // React Query также делает retry
  });

  return <div>{/* ... */}</div>;
}

Условная обработка

import { useErrorHandler } from '../hooks/useErrorHandler';
import { isCriticalError } from '../lib/api';
import api from '../lib/api';

function MyComponent() {
  const { handleError } = useErrorHandler();

  const loadData = async () => {
    try {
      const res = await api.get('/critical-data');
      setData(res.data);
    } catch (error) {
      if (isCriticalError(error)) {
        // Критичная ошибка - перенаправить
        handleError(error);
        navigate('/error');
      } else {
        // Некритичная - показать уведомление
        handleError(error);
      }
    }
  };

  return <button onClick={loadData}>Загрузить</button>;
}

Batch операции

import { useErrorHandler } from '../hooks/useErrorHandler';
import api from '../lib/api';

function MyComponent() {
  const { handleError, handleSuccess } = useErrorHandler();

  const deleteMultiple = async (ids) => {
    const errors = [];
    
    for (const id of ids) {
      try {
        await api.delete(`/domains-new/${id}`);
      } catch (error) {
        errors.push({ id, error });
      }
    }

    if (errors.length === 0) {
      handleSuccess(`Удалено ${ids.length} элементов`);
    } else {
      handleError(new Error(`Не удалось удалить ${errors.length} элементов`), {
        errors
      });
    }
  };

  return <button onClick={() => deleteMultiple([1, 2, 3])}>
    Удалить выбранные
  </button>;
}

📚 Дополнительная документация


🐛 Отладка

Посмотреть детали ошибки

В уведомлении кликните "Подробнее":

Детали:
{
  "type": "NETWORK",
  "status": null,
  "message": "Не удалось подключиться к серверу",
  "requestId": "abc123",
  "url": "/api/domains-new",
  "method": "GET",
  "timestamp": "2025-10-03T15:30:45.123Z",
  "action": "Проверьте подключение к интернету"
}

Консоль браузера

Все ошибки логируются в консоль:

// Открыть DevTools (F12) → Console
console.error('API Error:', {
  type: 'NETWORK',
  retryAttempt: 3,
  maxRetries: 3,
  // ...
});

Очистить кэш

// В консоли браузера
localStorage.clear();
sessionStorage.clear();
window.location.reload();

FAQ

Q: Нужно ли обрабатывать каждую ошибку вручную?
A: Нет! API автоматически показывает уведомления. Используйте handleError только для локальной логики.

Q: Как отключить автоматические уведомления?
A: Используйте withErrorHandler с опцией silent: true:

withErrorHandler(fn, { silent: true })

Q: Как изменить количество retry попыток?
A: Отредактируйте frontend/src/lib/apiErrorHandler.jsgetMaxRetries()

Q: Работает ли offline?
A: Частично. Приложение детектирует offline, но не сохраняет данные локально. Это можно добавить позже.

Q: Как интегрировать с Sentry?
A: См. ERROR_HANDLING_GUIDE.md → Раздел "Настройка"


Чек-лист интеграции

  • ErrorBoundary добавлен в App.jsx
  • NetworkErrorHandler добавлен в App.jsx
  • api.js обновлён с улучшенным retry
  • useErrorHandler хук создан
  • RetryButton компонент создан
  • apiErrorHandler утилиты созданы
  • Документация написана
  • Примеры кода добавлены
  • Линтер проверки пройдены

Статус: Готово к использованию!


🎉 Начните использовать прямо сейчас!

  1. Запустите приложение:

    cd backend && npm start
    cd frontend && npm run dev
    
  2. Импортируйте хук в любой компонент:

    import { useErrorHandler } from '../hooks/useErrorHandler';
    
  3. Используйте withErrorHandler или handleError

  4. Наслаждайтесь автоматической обработкой ошибок! 🚀


Вопросы?ERROR_HANDLING_GUIDE.md
Проблемы? → Проверьте консоль браузера
Идеи? → Создайте issue или PR