Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Zaawansowana obsługa błędów

W kosmosie rzeczy często nie ida zgodnie z planem - komunikacja może byc przerwana, serwery moga byc przeciazone, dane moga byc uszkodzone. Dobra obsługa błędów to różnica między udana misja a katastrofa!

Try/Catch/Finally - pelen wzorzec

1async function fetchWithFullErrorHandling(url) {
2  try {
3    // Proba pobrania danych
4    const response = await fetch(url);
5
6    if (!response.ok) {
7      // Rozne komunikaty dla różnych kodow błędów
8      switch (response.status) {
9        case 400:
10          throw new Error('Nieprawidlowe zapytanie');
11        case 401:
12          throw new Error('Brak autoryzacji - zaloguj sie');
13        case 403:
14          throw new Error('Brak dostepu do tego zasobu');
15        case 404:
16          throw new Error('Zasob nie zostal znaleziony');
17        case 429:
18          throw new Error('Zbyt wiele zapytan - sprobuj pozniej');
19        case 500:
20          throw new Error('Błąd serwera - spróbuj później');
21        default:
22          throw new Error(\`Nieznany błąd: \${response.status}\`);
23      }
24    }
25
26    return await response.json();
27  } catch (err) {
28    // Obsluga błędów sieci (brak internetu, timeout)
29    if (err instanceof TypeError) {
30      throw new Error(
31        'Błąd sieci - sprawdź połączenie z internetem'
32      );
33    }
34    throw err; // Re-throw inne błędy
35  }
36}

Komponent ErrorBoundary

React oferuje Error Boundaries - komponenty, które lapią błędy renderowania w swoich dzieciach:

1import React from 'react';
2
3class ErrorBoundary extends React.Component {
4  constructor(props) {
5    super(props);
6    this.state = { hasError: false, error: null };
7  }
8
9  static getDerivedStateFromError(error) {
10    return { hasError: true, error };
11  }
12
13  componentDidCatch(error, errorInfo) {
14    console.error('ErrorBoundary złapał błąd:', error);
15    console.error('Stack:', errorInfo.componentStack);
16  }
17
18  render() {
19    if (this.state.hasError) {
20      return (
21        <div className="error-boundary">
22          <h2>Cos poszlo nie tak</h2>
23          <p>{this.state.error?.message}</p>
24          <button onClick={() => this.setState({
25            hasError: false, error: null
26          })}>
27            Sprobuj ponownie
28          </button>
29        </div>
30      );
31    }
32
33    return this.props.children;
34  }
35}
36
37// Uzycie:
38function App() {
39  return (
40    <ErrorBoundary>
41      <SpaceDashboard />
42    </ErrorBoundary>
43  );
44}

Powiadomienia Toast

Zamiast wyswietlac błędy w miejscu komponentu, można użyć powiadomien toast:

1import React, { useState, createContext, useContext } from 'react';
2
3const ToastContext = createContext();
4
5function ToastProvider({ children }) {
6  const [toasts, setToasts] = useState([]);
7
8  const addToast = (message, type = 'error') => {
9    const id = Date.now();
10    setToasts(prev => [...prev, { id, message, type }]);
11    // Auto-usuwanie po 5 sekundach
12    setTimeout(() => {
13      setToasts(prev => prev.filter(t => t.id !== id));
14    }, 5000);
15  };
16
17  return (
18    <ToastContext.Provider value={{ addToast }}>
19      {children}
20      <div className="toast-container">
21        {toasts.map(toast => (
22          <div key={toast.id} className={\`toast toast-\${toast.type}\`}>
23            {toast.message}
24          </div>
25        ))}
26      </div>
27    </ToastContext.Provider>
28  );
29}
30
31function useToast() {
32  return useContext(ToastContext);
33}
34
35// Uzycie w komponencie:
36function DataComponent() {
37  const { addToast } = useToast();
38
39  const fetchData = async () => {
40    try {
41      const response = await fetch('/api/data');
42      if (!response.ok) throw new Error('Blad serwera');
43      const data = await response.json();
44      addToast('Dane pobrane pomyslnie!', 'success');
45    } catch (err) {
46      addToast(err.message, 'error');
47    }
48  };
49
50  return <button onClick={fetchData}>Pobierz dane</button>;
51}

Retry z exponential backoff

Gdy serwer jest przeciazony, warto czekac coraz dłużej między kolejnymi probami:

1async function fetchWithRetry(url, maxRetries = 3) {
2  for (let attempt = 0; attempt <= maxRetries; attempt++) {
3    try {
4      const response = await fetch(url);
5
6      if (response.status === 429 || response.status >= 500) {
7        // Serwer przeciazony lub błąd serwera
8        if (attempt < maxRetries) {
9          // Exponential backoff: 1s, 2s, 4s
10          const delay = Math.pow(2, attempt) * 1000;
11          console.log(
12            \`Proba \${attempt + 1} nie powiodla sie. \
13Ponowna proba za \${delay}ms...\`
14          );
15          await new Promise(resolve =>
16            setTimeout(resolve, delay)
17          );
18          continue;
19        }
20      }
21
22      if (!response.ok) {
23        throw new Error(\`HTTP Error: \${response.status}\`);
24      }
25
26      return await response.json();
27    } catch (err) {
28      if (attempt === maxRetries) {
29        throw new Error(
30          \`Nie udalo sie po \${maxRetries + 1} probach: \${err.message}\`
31        );
32      }
33
34      const delay = Math.pow(2, attempt) * 1000;
35      await new Promise(resolve => setTimeout(resolve, delay));
36    }
37  }
38}

Wyrazny feedback o błędach i mechanizmy naprawy to klucz do swietnego doświadczenia użytkownika. Nigdy nie zostawiaj użytkownika bez informacji o tym, co poszlo nie tak i co może zrobic!

Vai a CodeWorlds