feat: Добавление компонентов ErrorBoundary и NetworkErrorHandler для улучшения обработки ошибок в приложении. Увеличение таймаута запросов до 30 секунд и улучшение логики повторных попыток с детализированными уведомлениями об ошибках.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m43s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m43s
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
# ⚡ Быстрый старт - Обработка ошибок
|
||||
|
||||
## 5-минутное руководство по использованию новой системы обработки ошибок
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Основные сценарии
|
||||
|
||||
### 1️⃣ Простая загрузка данных
|
||||
|
||||
```jsx
|
||||
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
|
||||
|
||||
```jsx
|
||||
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️⃣ Ручная обработка ошибок
|
||||
|
||||
```jsx
|
||||
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`:
|
||||
|
||||
```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
|
||||
- Показывает красивую страницу ошибки
|
||||
- Кнопки восстановления
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Типы уведомлений
|
||||
|
||||
```jsx
|
||||
const { handleSuccess, handleError, handleWarning, handleInfo } = useErrorHandler();
|
||||
|
||||
handleSuccess('Данные сохранены'); // 🟢 Зелёное
|
||||
handleError('Не удалось загрузить'); // 🔴 Красное
|
||||
handleWarning('Изменения не сохранены'); // 🟡 Жёлтое
|
||||
handleInfo('Проверьте обновления'); // 🔵 Синее
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Продвинутые примеры
|
||||
|
||||
### React Query интеграция
|
||||
|
||||
```jsx
|
||||
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>;
|
||||
}
|
||||
```
|
||||
|
||||
### Условная обработка
|
||||
|
||||
```jsx
|
||||
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 операции
|
||||
|
||||
```jsx
|
||||
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>;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Дополнительная документация
|
||||
|
||||
- **Полное руководство:** [ERROR_HANDLING_GUIDE.md](./ERROR_HANDLING_GUIDE.md)
|
||||
- **Схемы работы:** [ERROR_HANDLING_FLOW.md](./ERROR_HANDLING_FLOW.md)
|
||||
- **Примеры кода:** [frontend/src/examples/ErrorHandlingExample.jsx](./frontend/src/examples/ErrorHandlingExample.jsx)
|
||||
- **Краткое резюме:** [ERROR_HANDLING_SUMMARY.md](./ERROR_HANDLING_SUMMARY.md)
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Отладка
|
||||
|
||||
### Посмотреть детали ошибки
|
||||
|
||||
В уведомлении кликните **"Подробнее"**:
|
||||
|
||||
```
|
||||
Детали:
|
||||
{
|
||||
"type": "NETWORK",
|
||||
"status": null,
|
||||
"message": "Не удалось подключиться к серверу",
|
||||
"requestId": "abc123",
|
||||
"url": "/api/domains-new",
|
||||
"method": "GET",
|
||||
"timestamp": "2025-10-03T15:30:45.123Z",
|
||||
"action": "Проверьте подключение к интернету"
|
||||
}
|
||||
```
|
||||
|
||||
### Консоль браузера
|
||||
|
||||
Все ошибки логируются в консоль:
|
||||
|
||||
```javascript
|
||||
// Открыть DevTools (F12) → Console
|
||||
console.error('API Error:', {
|
||||
type: 'NETWORK',
|
||||
retryAttempt: 3,
|
||||
maxRetries: 3,
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Очистить кэш
|
||||
|
||||
```javascript
|
||||
// В консоли браузера
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
window.location.reload();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ❓ FAQ
|
||||
|
||||
**Q: Нужно ли обрабатывать каждую ошибку вручную?**
|
||||
A: Нет! API автоматически показывает уведомления. Используйте `handleError` только для локальной логики.
|
||||
|
||||
**Q: Как отключить автоматические уведомления?**
|
||||
A: Используйте `withErrorHandler` с опцией `silent: true`:
|
||||
```jsx
|
||||
withErrorHandler(fn, { silent: true })
|
||||
```
|
||||
|
||||
**Q: Как изменить количество retry попыток?**
|
||||
A: Отредактируйте `frontend/src/lib/apiErrorHandler.js` → `getMaxRetries()`
|
||||
|
||||
**Q: Работает ли offline?**
|
||||
A: Частично. Приложение детектирует offline, но не сохраняет данные локально. Это можно добавить позже.
|
||||
|
||||
**Q: Как интегрировать с Sentry?**
|
||||
A: См. [ERROR_HANDLING_GUIDE.md](./ERROR_HANDLING_GUIDE.md) → Раздел "Настройка"
|
||||
|
||||
---
|
||||
|
||||
## ✅ Чек-лист интеграции
|
||||
|
||||
- [x] ErrorBoundary добавлен в App.jsx
|
||||
- [x] NetworkErrorHandler добавлен в App.jsx
|
||||
- [x] api.js обновлён с улучшенным retry
|
||||
- [x] useErrorHandler хук создан
|
||||
- [x] RetryButton компонент создан
|
||||
- [x] apiErrorHandler утилиты созданы
|
||||
- [x] Документация написана
|
||||
- [x] Примеры кода добавлены
|
||||
- [x] Линтер проверки пройдены
|
||||
|
||||
**Статус: ✅ Готово к использованию!**
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Начните использовать прямо сейчас!
|
||||
|
||||
1. Запустите приложение:
|
||||
```powershell
|
||||
cd backend && npm start
|
||||
cd frontend && npm run dev
|
||||
```
|
||||
|
||||
2. Импортируйте хук в любой компонент:
|
||||
```jsx
|
||||
import { useErrorHandler } from '../hooks/useErrorHandler';
|
||||
```
|
||||
|
||||
3. Используйте `withErrorHandler` или `handleError`
|
||||
|
||||
4. Наслаждайтесь автоматической обработкой ошибок! 🚀
|
||||
|
||||
---
|
||||
|
||||
**Вопросы?** → [ERROR_HANDLING_GUIDE.md](./ERROR_HANDLING_GUIDE.md)
|
||||
**Проблемы?** → Проверьте консоль браузера
|
||||
**Идеи?** → Создайте issue или PR
|
||||
|
||||
Reference in New Issue
Block a user