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!
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}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}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".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!