# πŸ›‘οΈ Руководство ΠΏΠΎ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ ошибок КомплСксная систСма ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ошибок для Router Lists UI, Π²ΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‰Π°Ρ автоматичСский retry, user-friendly сообщСния ΠΈ ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ состояния сСти. ## πŸ“š ОглавлСниС - [ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ систСмы](#ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹-систСмы) - [Π’ΠΈΠΏΡ‹ ошибок](#Ρ‚ΠΈΠΏΡ‹-ошибок) - [АвтоматичСский Retry](#автоматичСский-retry) - [ИспользованиС Π² ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°Ρ…](#использованиС-Π²-ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°Ρ…) - [API Reference](#api-reference) - [ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹](#ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹) - [Π›ΡƒΡ‡ΡˆΠΈΠ΅ ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠΈ](#Π»ΡƒΡ‡ΡˆΠΈΠ΅-ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠΈ) --- ## 🧩 ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ систСмы ### 1. **ErrorBoundary** Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ошибок React для Π»ΠΎΠ²Π»ΠΈ ошибок Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°. **ВозмоТности:** - βœ… Π›ΠΎΠ²ΠΈΡ‚ ошибки Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π° ΠΈ ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ fallback UI - βœ… Π›ΠΎΠ³ΠΈΡ€ΡƒΠ΅Ρ‚ ошибки Π² консоль ΠΈ систСму ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³Π° - βœ… ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ Π΄Π΅Ρ‚Π°Π»ΠΈ ошибки Π² dev Ρ€Π΅ΠΆΠΈΠΌΠ΅ - βœ… АвтоматичСски ΠΎΡ‡ΠΈΡ‰Π°Π΅Ρ‚ кэш ΠΏΡ€ΠΈ ΠΏΠΎΠ²Ρ‚ΠΎΡ€ΡΡŽΡ‰ΠΈΡ…ΡΡ ΠΎΡˆΠΈΠ±ΠΊΠ°Ρ… - βœ… Кнопки восстановлСния: "ΠŸΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ снова", "ΠŸΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ", "На Π³Π»Π°Π²Π½ΡƒΡŽ" **ИспользованиС:** ```jsx // Π£ΠΆΠ΅ встроСн Π² App.jsx, ΠΎΠ±ΠΎΡ€Π°Ρ‡ΠΈΠ²Π°Π΅Ρ‚ всС ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ``` ### 2. **NetworkErrorHandler** ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ для ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³Π° состояния сСти. **ВозмоТности:** - βœ… Π”Π΅Ρ‚Π΅ΠΊΡ‚ΠΈΡ€ΡƒΠ΅Ρ‚ offline/online события - βœ… ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ Π±Π°Π½Π½Π΅Ρ€ ΠΏΡ€ΠΈ ΠΏΠΎΡ‚Π΅Ρ€Π΅ соСдинСния - βœ… УвСдомляСт ΠΎ восстановлСнии соСдинСния - βœ… Π˜Π½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ΡΡ с глобальной систСмой ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ **ИспользованиС:** ```jsx // Π£ΠΆΠ΅ встроСн Π² App.jsx ``` ### 3. **apiErrorHandler.js** Π£Ρ‚ΠΈΠ»ΠΈΡ‚Ρ‹ для классификации ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ошибок API. **Π€ΡƒΠ½ΠΊΡ†ΠΈΠΈ:** - `getErrorType(error)` - опрСдСляСт Ρ‚ΠΈΠΏ ошибки - `isRetriableError(error, method)` - провСряСт Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ retry - `getRetryDelay(attemptNumber)` - вычисляСт Π·Π°Π΄Π΅Ρ€ΠΆΠΊΡƒ для retry - `getMaxRetries(errorType, method)` - Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ максимум ΠΏΠΎΠΏΡ‹Ρ‚ΠΎΠΊ - `formatErrorMessage(error)` - Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ user-friendly сообщСниС - `getErrorDetails(error)` - ΠΈΠ·Π²Π»Π΅ΠΊΠ°Π΅Ρ‚ Π΄Π΅Ρ‚Π°Π»ΠΈ ошибки - `getErrorAction(error)` - Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°Ρ†ΠΈΠΈ ΠΏΠΎ ΡƒΡΡ‚Ρ€Π°Π½Π΅Π½ΠΈΡŽ - `isCriticalError(error)` - провСряСт ΠΊΡ€ΠΈΡ‚ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒ ошибки - `logError(error, context)` - Π»ΠΎΠ³ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΎΡˆΠΈΠ±ΠΊΡƒ ### 4. **useErrorHandler** React Ρ…ΡƒΠΊ для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ошибок Π² ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°Ρ…. **API:** ```javascript const { handleError, // ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ ΠΎΡˆΠΈΠ±ΠΊΡƒ handleSuccess, // ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ успСх handleWarning, // ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΏΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΠ΅ handleInfo, // ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΈΠ½Ρ„ΠΎ withErrorHandler // ΠžΠ±Ρ‘Ρ€Ρ‚ΠΊΠ° для async Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ } = useErrorHandler(); ``` ### 5. **RetryButton** Кнопка для ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎΠΉ ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠΈ с ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ†ΠΈΠ΅ΠΉ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ. **Props:** - `onRetry` - async функция для выполнСния - `loading` - внСшнСС состояниС Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ - `disabled` - ΠΎΡ‚ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ ΠΊΠ½ΠΎΠΏΠΊΡƒ - `className` - CSS классы - `showIcon` - ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ ΠΈΠΊΠΎΠ½ΠΊΡƒ - `children` - тСкст ΠΊΠ½ΠΎΠΏΠΊΠΈ --- ## 🎭 Π’ΠΈΠΏΡ‹ ошибок БистСма классифицируСт ошибки Π½Π° ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ Ρ‚ΠΈΠΏΡ‹: | Π’ΠΈΠΏ | Код | ОписаниС | Retry | |-----|-----|----------|-------| | **NETWORK** | - | ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ с ΡΠ΅Ρ‚ΡŒΡŽ (Π½Π΅Ρ‚ соСдинСния) | βœ… Π”Π° (3x) | | **TIMEOUT** | ECONNABORTED | ΠŸΡ€Π΅Π²Ρ‹ΡˆΠ΅Π½ΠΎ врСмя оТидания | βœ… Π”Π° (2x) | | **SERVER** | 5xx | Ошибка Π½Π° сСрвСрС | βœ… Π”Π° (2x GET) | | **CLIENT** | 4xx | Ошибка ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° (общая) | ❌ НСт | | **VALIDATION** | 400 | Ошибка Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ Π΄Π°Π½Π½Ρ‹Ρ… | ❌ НСт | | **AUTH** | 401 | ВрСбуСтся аутСнтификация | ❌ НСт | | **PERMISSION** | 403 | НСт ΠΏΡ€Π°Π² доступа | ❌ НСт | | **NOT_FOUND** | 404 | РСсурс Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ | ❌ НСт | | **CONFLICT** | 409 | ΠšΠΎΠ½Ρ„Π»ΠΈΠΊΡ‚ Π΄Π°Π½Π½Ρ‹Ρ… (ETag) | ❌ НСт | | **RATE_LIMIT** | 429 | ΠŸΡ€Π΅Π²Ρ‹ΡˆΠ΅Π½ Π»ΠΈΠΌΠΈΡ‚ запросов | ❌ НСт | | **UNKNOWN** | - | НСизвСстная ошибка | ❌ НСт | --- ## πŸ”„ АвтоматичСский Retry ### БтратСгия Retry **GET запросы:** - Network Error: Π΄ΠΎ 3 ΠΏΠΎΠΏΡ‹Ρ‚ΠΎΠΊ - Timeout: Π΄ΠΎ 2 ΠΏΠΎΠΏΡ‹Ρ‚ΠΎΠΊ - Server Error (5xx): Π΄ΠΎ 2 ΠΏΠΎΠΏΡ‹Ρ‚ΠΎΠΊ **POST/PUT/PATCH/DELETE:** - Network Error: Π΄ΠΎ 1 ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠΈ - Timeout: Π΄ΠΎ 1 ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠΈ - Π‘Π΅Π· retry для 5xx (Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚Ρ‹) ### Exponential Backoff Π—Π°Π΄Π΅Ρ€ΠΆΠΊΠΈ ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ°ΠΌΠΈ растут ΡΠΊΡΠΏΠΎΠ½Π΅Π½Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎ: ``` ΠŸΠΎΠΏΡ‹Ρ‚ΠΊΠ° 1: 300ms (Β± 20% jitter) ΠŸΠΎΠΏΡ‹Ρ‚ΠΊΠ° 2: 600ms (Β± 20% jitter) ΠŸΠΎΠΏΡ‹Ρ‚ΠΊΠ° 3: 1200ms (Β± 20% jitter) ``` **Jitter** (Β±20%) добавляСтся для избСТания thundering herd problem. ### Максимальная Π·Π°Π΄Π΅Ρ€ΠΆΠΊΠ° Максимальная Π·Π°Π΄Π΅Ρ€ΠΆΠΊΠ° ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π° **10 сСкундами** для прСдотвращСния бСсконСчного оТидания. --- ## πŸ’» ИспользованиС Π² ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°Ρ… ### Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ 1: Ручная ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ```jsx import { useState } from 'react'; import { useErrorHandler } from '../hooks/useErrorHandler'; import api from '../lib/api'; function MyComponent() { const [data, setData] = useState(null); const { handleError, handleSuccess } = useErrorHandler(); const fetchData = async () => { try { const response = await api.get('/domains-new'); setData(response.data); handleSuccess('Π”Π°Π½Π½Ρ‹Π΅ Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Ρ‹'); } catch (error) { handleError(error, { component: 'MyComponent' }); } }; return ( ); } ``` ### Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ 2: АвтоматичСская ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ```jsx import { useState } from 'react'; import { useErrorHandler } from '../hooks/useErrorHandler'; import api from '../lib/api'; function MyComponent() { const [data, setData] = useState(null); const { withErrorHandler } = useErrorHandler(); const fetchData = withErrorHandler( async () => { const response = await api.get('/domains-new'); setData(response.data); }, { successMessage: 'Π”Π°Π½Π½Ρ‹Π΅ Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Ρ‹', context: { component: 'MyComponent' } } ); return ( ); } ``` ### Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ 3: Π‘ RetryButton ```jsx import RetryButton from '../components/RetryButton'; import api from '../lib/api'; function MyComponent() { const saveData = async () => { await api.post('/domains-new', { domains: [...] }); }; return ( Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ ); } ``` ### Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ 4: 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 response = await api.get('/domains-new'); return response.data; }, onError: (error) => { handleError(error, { component: 'MyComponent' }); } }); // React Query автоматичСски Π΄Π΅Π»Π°Π΅Ρ‚ retry, // Π½ΠΎ ΠΌΡ‹ добавляСм user-friendly увСдомлСния return
{/* ... */}
; } ``` --- ## πŸ“– API Reference ### useErrorHandler() ```typescript interface ErrorHandlerHook { // ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ ΠΎΡˆΠΈΠ±ΠΊΡƒ handleError: (error: Error, context?: object) => void; // ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ успСх handleSuccess: (message?: string) => void; // ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΏΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΠ΅ handleWarning: (message: string, details?: object) => void; // ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ handleInfo: (message: string, details?: object) => void; // ΠžΠ±Ρ‘Ρ€Ρ‚ΠΊΠ° для async Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ withErrorHandler: ( asyncFn: Function, options?: { successMessage?: string; context?: object; silent?: boolean; rethrow?: boolean; defaultValue?: any; } ) => Function; } ``` ### getErrorType() ```javascript import { getErrorType, ErrorType } from '../lib/api'; const error = new Error('Network error'); const type = getErrorType(error); if (type === ErrorType.NETWORK) { console.log('ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ с ΡΠ΅Ρ‚ΡŒΡŽ'); } ``` ### formatErrorMessage() ```javascript import { formatErrorMessage } from '../lib/api'; try { await api.get('/endpoint'); } catch (error) { const message = formatErrorMessage(error); // "НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒΡΡ ΠΊ сСрвСру. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ соСдинСниС." alert(message); } ``` ### getErrorAction() ```javascript import { getErrorAction } from '../lib/api'; try { await api.post('/endpoint', data); } catch (error) { const action = getErrorAction(error); // "ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎΡΡ‚ΡŒ Π²Π²Π΅Π΄Ρ‘Π½Π½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ…." console.log('РСкомСндация:', action); } ``` --- ## 🎯 ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ ### ΠŸΡ€ΠΈΠΌΠ΅Ρ€ 1: Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π΄Π°Π½Π½Ρ‹Ρ… с ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΎΠΉ ошибок ```jsx import { useState } from 'react'; import { useErrorHandler } from '../hooks/useErrorHandler'; import RetryButton from '../components/RetryButton'; import api from '../lib/api'; function DataLoader() { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const { withErrorHandler } = useErrorHandler(); const loadData = withErrorHandler( async () => { setLoading(true); try { const response = await api.get('/domains-new'); setData(response.data); } finally { setLoading(false); } }, { successMessage: 'Π”Π°Π½Π½Ρ‹Π΅ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Ρ‹', context: { component: 'DataLoader' } } ); return (
{!data ? ( Π—Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ ) : (

Π—Π°Π³Ρ€ΡƒΠΆΠ΅Π½ΠΎ: {data.items.length} записСй

)}
); } ``` ### ΠŸΡ€ΠΈΠΌΠ΅Ρ€ 2: Π€ΠΎΡ€ΠΌΠ° с Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠ΅ΠΉ ```jsx import { useState } from 'react'; import { useErrorHandler } from '../hooks/useErrorHandler'; import api from '../lib/api'; function DomainForm() { const [domain, setDomain] = useState(''); const { handleError, handleSuccess } = useErrorHandler(); const handleSubmit = async (e) => { e.preventDefault(); try { await api.post('/domains-new', { domains: [{ domain, community: '65000:100' }] }); handleSuccess('Π”ΠΎΠΌΠ΅Π½ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½'); setDomain(''); } catch (error) { // БистСма автоматичСски ΠΏΠΎΠΊΠ°ΠΆΠ΅Ρ‚ user-friendly сообщСниС // "Ошибка Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ Π΄Π°Π½Π½Ρ‹Ρ…. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ Π²Π²Π΅Π΄Ρ‘Π½Π½Ρ‹Π΅ значСния." handleError(error, { form: 'DomainForm', domain }); } }; return (
setDomain(e.target.value)} placeholder="example.com" />
); } ``` ### ΠŸΡ€ΠΈΠΌΠ΅Ρ€ 3: ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ΠΊΡ€ΠΈΡ‚ΠΈΡ‡Π½Ρ‹Ρ… ошибок ```jsx import { useEffect } from 'react'; import { useErrorHandler } from '../hooks/useErrorHandler'; import { isCriticalError } from '../lib/api'; import api from '../lib/api'; function CriticalDataLoader() { const { handleError } = useErrorHandler(); useEffect(() => { const loadCriticalData = async () => { try { await api.get('/server-configs'); } catch (error) { if (isCriticalError(error)) { // ΠšΡ€ΠΈΡ‚ΠΈΡ‡Π½Π°Ρ ошибка - ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ модальноС ΠΎΠΊΠ½ΠΎ handleError(error); // МоТно Ρ‚Π°ΠΊΠΆΠ΅ ΠΏΠ΅Ρ€Π΅Π½Π°ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π½Π° страницу ошибки // window.location.href = '/error'; } } }; loadCriticalData(); }, [handleError]); return
...
; } ``` --- ## βœ… Π›ΡƒΡ‡ΡˆΠΈΠ΅ ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠΈ ### 1. ВсСгда ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ useErrorHandler ```jsx // βœ… Π₯ΠΎΡ€ΠΎΡˆΠΎ import { useErrorHandler } from '../hooks/useErrorHandler'; function MyComponent() { const { handleError } = useErrorHandler(); // ... } // ❌ ΠŸΠ»ΠΎΡ…ΠΎ function MyComponent() { const handleError = (error) => { alert(error.message); // НС user-friendly }; } ``` ### 2. ΠŸΠ΅Ρ€Π΅Π΄Π°Π²Π°ΠΉΡ‚Π΅ контСкст Π² handleError ```jsx // βœ… Π₯ΠΎΡ€ΠΎΡˆΠΎ handleError(error, { component: 'DomainsManager', action: 'delete', itemId: domain.id }); // ❌ ΠŸΠ»ΠΎΡ…ΠΎ handleError(error); ``` ### 3. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ withErrorHandler для простых случаСв ```jsx // βœ… Π₯ΠΎΡ€ΠΎΡˆΠΎ - Π»Π°ΠΊΠΎΠ½ΠΈΡ‡Π½ΠΎ const loadData = withErrorHandler( async () => { const res = await api.get('/data'); setData(res.data); }, { successMessage: 'Π—Π°Π³Ρ€ΡƒΠΆΠ΅Π½ΠΎ' } ); // ❌ Π˜Π·Π±Ρ‹Ρ‚ΠΎΡ‡Π½ΠΎ const loadData = async () => { try { const res = await api.get('/data'); setData(res.data); handleSuccess('Π—Π°Π³Ρ€ΡƒΠΆΠ΅Π½ΠΎ'); } catch (error) { handleError(error); } }; ``` ### 4. НС Π΄ΡƒΠ±Π»ΠΈΡ€ΡƒΠΉΡ‚Π΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ошибок ```jsx // βœ… Π₯ΠΎΡ€ΠΎΡˆΠΎ - API автоматичСски ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ try { await api.post('/data', payload); } catch (error) { // Ошибка ΡƒΠΆΠ΅ ΠΏΠΎΠΊΠ°Π·Π°Π½Π° ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŽ // Π—Π΄Π΅ΡΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ локальная ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° setLoading(false); } // ❌ ΠŸΠ»ΠΎΡ…ΠΎ - Π΄Π²ΠΎΠΉΠ½ΠΎΠ΅ ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ try { await api.post('/data', payload); } catch (error) { handleError(error); // Π£Π²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠΊΠ°Π·Π°Π½ΠΎ Π΄Π²Π°ΠΆΠ΄Ρ‹! } ``` ### 5. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ RetryButton для ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ ```jsx // βœ… Π₯ΠΎΡ€ΠΎΡˆΠΎ Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ // ❌ ΠŸΠ»ΠΎΡ…ΠΎ - ручная рСализация retry ``` ### 6. Π›ΠΎΠ³ΠΈΡ€ΡƒΠΉΡ‚Π΅ ΠΊΡ€ΠΈΡ‚ΠΈΡ‡Π½Ρ‹Π΅ ошибки ```jsx // βœ… Π₯ΠΎΡ€ΠΎΡˆΠΎ import { isCriticalError, logError } from '../lib/api'; catch (error) { if (isCriticalError(error)) { logError(error, { critical: true, user: currentUser }); } handleError(error); } ``` --- ## πŸ”§ Настройка ### ИзмСнСниС Ρ‚Π°ΠΉΠΌΠ°ΡƒΡ‚Π° ```javascript // frontend/src/lib/api.js const api = axios.create({ timeout: 30000, // 30 сСкунд (ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ) }); ``` ### Настройка retry ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² ```javascript // frontend/src/lib/apiErrorHandler.js // Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π±Π°Π·ΠΎΠ²ΡƒΡŽ Π·Π°Π΄Π΅Ρ€ΠΆΠΊΡƒ export function getRetryDelay(attemptNumber, baseDelay = 300) { // МСняСм baseDelay для Π΄Ρ€ΡƒΠ³ΠΎΠΉ стратСгии } // Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ количСство ΠΏΠΎΠΏΡ‹Ρ‚ΠΎΠΊ export function getMaxRetries(errorType, method = 'GET') { if (method === 'GET') { return errorType === ErrorType.NETWORK ? 5 : 3; // Π‘ΠΎΠ»ΡŒΡˆΠ΅ ΠΏΠΎΠΏΡ‹Ρ‚ΠΎΠΊ } // ... } ``` ### Π˜Π½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΡ с Sentry ```javascript // frontend/src/lib/apiErrorHandler.js export function logError(error, context = {}) { // ... // Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΡƒ Π² Sentry if (window.Sentry && isCriticalError(error)) { window.Sentry.captureException(error, { contexts: { api: getErrorDetails(error), custom: context }, level: 'error' }); } } ``` --- ## πŸŽ“ Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ рСсурсы - [ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ использования](./frontend/src/examples/ErrorHandlingExample.jsx) - [Axios докумСнтация](https://axios-http.com/docs/handling_errors) - [React Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) - [Exponential Backoff](https://en.wikipedia.org/wiki/Exponential_backoff) --- ## πŸ“ Changelog ### v1.0.0 (2025-10-03) - βœ… Π”ΠΎΠ±Π°Π²Π»Π΅Π½ ErrorBoundary для React ошибок - βœ… Π”ΠΎΠ±Π°Π²Π»Π΅Π½ NetworkErrorHandler для offline/online - βœ… Π£Π»ΡƒΡ‡ΡˆΠ΅Π½ retry ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ с exponential backoff - βœ… Π”ΠΎΠ±Π°Π²Π»Π΅Π½Π° типизация ошибок (11 Ρ‚ΠΈΠΏΠΎΠ²) - βœ… User-friendly сообщСния вмСсто тСхничСских - βœ… Π₯ΡƒΠΊ useErrorHandler для ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² - βœ… ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ RetryButton - βœ… Π˜Π½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΡ с систСмой ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ - βœ… Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΡ€ΠΈΡ‚ΠΈΡ‡Π½Ρ‹Ρ… ошибок - βœ… Π£Π²Π΅Π»ΠΈΡ‡Π΅Π½ Ρ‚Π°ΠΉΠΌΠ°ΡƒΡ‚ Π΄ΠΎ 30 сСкунд --- ## 🀝 ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° Если Ρƒ вас Π²ΠΎΠ·Π½ΠΈΠΊΠ»ΠΈ вопросы ΠΈΠ»ΠΈ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ с систСмой ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ошибок: 1. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ консоль Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π° Π½Π° Π½Π°Π»ΠΈΡ‡ΠΈΠ΅ ошибок 2. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ Π²ΠΊΠ»Π°Π΄ΠΊΡƒ Network Π² DevTools 3. Π£Π±Π΅Π΄ΠΈΡ‚Π΅ΡΡŒ Ρ‡Ρ‚ΠΎ backend сСрвСр Π·Π°ΠΏΡƒΡ‰Π΅Π½ 4. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ настройки CORS **ΠŸΠΎΠ»Π΅Π·Π½Ρ‹Π΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ для ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ:** ```javascript // Π’ консоли Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π° localStorage.clear(); // ΠžΡ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ кэш window.notify.clear(); // ΠžΡ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ увСдомлСния console.log(window.notify); // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ систСму ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ ```