Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Stany ładowania, błędów i pustych danych

Gdy nasz statek kosmiczny wysyła zapytanie do Centrum Dowodzenia, może sie wydarzyc kilka rzeczy: dane sa w drodze (ładowanie), przeslanie sie nie powiodlo (błąd), albo centrum nie ma zadnych danych do wyslania (pusty wynik). Musimy obsługiwać każda z tych sytuacji!

Wzorzec Loading/Error/Data

Najpopularniejszy wzorzec w React do obsługi pobierania danych uzywa trzech zmiennych stanu:

1import React, { useState, useEffect } from 'react';
2
3function StarshipCatalog() {
4  const [data, setData] = useState(null);
5  const [loading, setLoading] = useState(true);
6  const [error, setError] = useState(null);
7
8  useEffect(() => {
9    async function fetchStarships() {
10      try {
11        setLoading(true);
12        setError(null);
13
14        const response = await fetch(
15          'https://swapi.dev/api/starships/'
16        );
17
18        if (!response.ok) {
19          throw new Error(\`HTTP Error: \${response.status}\`);
20        }
21
22        const result = await response.json();
23        setData(result.results);
24      } catch (err) {
25        setError(err.message);
26      } finally {
27        setLoading(false);
28      }
29    }
30
31    fetchStarships();
32  }, []);
33
34  // Stan ładowania
35  if (loading) {
36    return <div className="loading">Skanowanie sektora...</div>;
37  }
38
39  // Stan błędu
40  if (error) {
41    return <div className="error">Blad komunikacji: {error}</div>;
42  }
43
44  // Stan pustych danych
45  if (!data || data.length === 0) {
46    return <div className="empty">Brak statkow w tym sektorze</div>;
47  }
48
49  // Dane gotowe do wyświetlenia
50  return (
51    <div>
52      <h2>Wykryte statki: {data.length}</h2>
53      {data.map(ship => (
54        <div key={ship.name}>
55          <h3>{ship.name}</h3>
56          <p>Model: {ship.model}</p>
57        </div>
58      ))}
59    </div>
60  );
61}

Warunkowe renderowanie stanow

Możemy tworzyć ladniejsze komponenty dla każdego stanu:

1function LoadingSpinner() {
2  return (
3    <div className="spinner-container">
4      <div className="spinner"></div>
5      <p>Ladowanie danych z centrum dowodzenia...</p>
6    </div>
7  );
8}
9
10function ErrorMessage({ message, onRetry }) {
11  return (
12    <div className="error-container">
13      <h3>Utracono połączenie!</h3>
14      <p>{message}</p>
15      <button onClick={onRetry}>
16        Ponow probe
17      </button>
18    </div>
19  );
20}
21
22function EmptyState() {
23  return (
24    <div className="empty-container">
25      <p>Brak danych w tym kwadrancie galaktyki</p>
26      <p>Sprobuj przeskanowac inny sektor</p>
27    </div>
28  );
29}

Mechanizm ponownej proby (Retry)

Gdy komunikacja z serwerem sie nie powiedzie, warto dac użytkownikowi możliwość ponowienia proby:

1function SpaceData() {
2  const [data, setData] = useState(null);
3  const [loading, setLoading] = useState(true);
4  const [error, setError] = useState(null);
5
6  const fetchData = async () => {
7    try {
8      setLoading(true);
9      setError(null);
10      const response = await fetch(
11        'https://swapi.dev/api/people/'
12      );
13      if (!response.ok) throw new Error('Blad sieci');
14      const result = await response.json();
15      setData(result.results);
16    } catch (err) {
17      setError(err.message);
18    } finally {
19      setLoading(false);
20    }
21  };
22
23  useEffect(() => {
24    fetchData();
25  }, []);
26
27  if (loading) return <LoadingSpinner />;
28  if (error) return <ErrorMessage message={error} onRetry={fetchData} />;
29  if (!data || data.length === 0) return <EmptyState />;
30
31  return (
32    <ul>
33      {data.map(person => (
34        <li key={person.name}>{person.name}</li>
35      ))}
36    </ul>
37  );
38}

Kluczowa różnica: funkcja

fetchData
jest zdefiniowana poza
useEffect
, dzięki czemu możemy ja wywołać ponownie z przycisku "Ponow probe".

Szkieletowe ładowanie (Skeleton Loading)

Zamiast prostego spinnera, można wyświetlić szkielet interfejsu:

1function SkeletonCard() {
2  return (
3    <div className="skeleton-card">
4      <div className="skeleton-title"></div>
5      <div className="skeleton-text"></div>
6      <div className="skeleton-text short"></div>
7    </div>
8  );
9}
10
11function PlanetCatalog() {
12  const [planets, setPlanets] = useState(null);
13  const [loading, setLoading] = useState(true);
14
15  // ... fetch logic ...
16
17  if (loading) {
18    return (
19      <div>
20        <SkeletonCard />
21        <SkeletonCard />
22        <SkeletonCard />
23      </div>
24    );
25  }
26
27  return (
28    <div>
29      {planets.map(planet => (
30        <PlanetCard key={planet.name} planet={planet} />
31      ))}
32    </div>
33  );
34}

Pamietaj -- kazdy stan (loading, error, success, empty) wymaga dedykowanego UI. Dobra obsluga stanow to roznica miedzy profesjonalna aplikacja a amatorskim projektem. Uzytkownik nigdy nie powinien patrzec na pusty ekran bez informacji o tym, co sie dzieje!

Vai a CodeWorlds