Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Przekazywanie stanu przez lokalizacje w międzygalaktycznej komunikacji

W kosmicznej podróży, czasami potrzebujemy przesłać ważne dane z jednego sektora stacji kosmicznej do drugiego. W aplikacjach React wykorzystujących React Router podobnie przekazujemy stan (dane) między różnymi stronami za pomocą mechanizmu

location state
. Zobaczmy, jak wykorzystać ten mechanizm do usprawnienia naszej międzygalaktycznej komunikacji.

Czym jest Location State?

Location state to mechanizm w React Router, który pozwala na przekazywanie danych podczas nawigacji między różnymi ścieżkami bez konieczności używania parametrów URL, cookies, czy globalnego stanu aplikacji. Dane te nie są widoczne w URL, ale są dostępne na stronie docelowej.

Przekazywanie stanu przez Link

Najprostszym sposobem przekazania danych przez location state jest użycie komponentu

Link
z dodatkowym parametrem
state
:

1import { Link } from 'react-router-dom';
2
3function PlanetList() {
4  const planets = [
5    { id: 1, name: 'Mars', atmosphere: 'Cienka, głównie CO2', temperature: 'Od -125°C do 20°C' },
6    { id: 2, name: 'Wenus', atmosphere: 'Gęsta, głównie CO2', temperature: 'Średnio 462°C' },
7    { id: 3, name: 'Tytan', atmosphere: 'Gęsta, azot i metan', temperature: 'Średnio -179°C' }
8  ];
9
10  return (
11    <div className="planet-explorer">
12      <h1>Eksploracja Planet</h1>
13      <ul className="planet-list">
14        {planets.map(planet => (
15          <li key={planet.id}>
16            <Link
17              to={`/planet/${planet.id}`}
18              state={{ planetData: planet }}
19            >
20              Eksploruj {planet.name}
21            </Link>
22          </li>
23        ))}
24      </ul>
25    </div>
26  );
27}

W powyższym przykładzie, kliknięcie w link przeniesie użytkownika do strony

/planet/1
(dla Marsa) i jednocześnie przekaże obiekt
planetData
zawierający wszystkie informacje o planecie.

Odbieranie stanu w komponencie docelowym

Aby odczytać przekazany stan w komponencie docelowym, używamy hooka

useLocation
:

1import { useParams, useLocation } from 'react-router-dom';
2
3function PlanetDetails() {
4  // Pobieramy parametr z URL
5  const { id } = useParams();
6
7  // Pobieramy obiekt location, który zawiera przekazany stan
8  const location = useLocation();
9
10  // Wyciągamy dane planety z stanu lokalizacji, z fallbackiem na pusty obiekt
11  const { planetData } = location.state || {};
12
13  // Jeśli nie otrzymaliśmy danych planety, możemy pokazać komunikat o błędzie
14  // lub próbować pobrać dane na podstawie ID
15  if (!planetData) {
16    return (
17      <div className="error-message">
18        <h2>Brak danych planety</h2>
19        <p>Nie udało się wczytać danych dla planety o ID: {id}</p>
20        <Link to="/">Wróć do listy planet</Link>
21      </div>
22    );
23  }
24
25  return (
26    <div className="planet-details">
27      <h1>Szczegóły planety: {planetData.name}</h1>
28
29      <div className="planet-info">
30        <p><strong>Atmosfera:</strong> {planetData.atmosphere}</p>
31        <p><strong>Temperatura:</strong> {planetData.temperature}</p>
32      </div>
33
34      <Link to="/">Wróć do listy planet</Link>
35    </div>
36  );
37}

Programowe przekazywanie stanu

Możemy również przekazywać stan programowo za pomocą hooka

useNavigate
:

1import { useNavigate } from 'react-router-dom';
2
3function MissionControl() {
4  const navigate = useNavigate();
5
6  const startMission = (mission) => {
7    // Wykonujemy jakieś operacje przed przejściem do następnej strony
8    const missionData = {
9      ...mission,
10      startDate: new Date().toISOString(),
11      commander: 'Kapitan Alex Chen',
12      status: 'W trakcie'
13    };
14
15    // Przekierowujemy do strony misji z danymi
16    navigate(`/mission/${mission.id}`, {
17      state: { missionData }
18    });
19  };
20
21  return (
22    <div className="mission-control">
23      <h1>Centrum Dowodzenia Misjami</h1>
24
25      <div className="mission-list">
26        <div className="mission-card">
27          <h3>Misja: Eksploracja Marsa</h3>
28          <p>Cel: Badanie próbek gleby</p>
29          <button onClick={() => startMission({
30            id: 'mars-exploration-1',
31            name: 'Eksploracja Marsa',
32            objective: 'Badanie próbek gleby'
33          })}>
34            Rozpocznij misję
35          </button>
36        </div>
37
38        <div className="mission-card">
39          <h3>Misja: Stacja Satelitarna</h3>
40          <p>Cel: Naprawa układów komunikacyjnych</p>
41          <button onClick={() => startMission({
42            id: 'satellite-repair-1',
43            name: 'Stacja Satelitarna',
44            objective: 'Naprawa układów komunikacyjnych'
45          })}>
46            Rozpocznij misję
47          </button>
48        </div>
49      </div>
50    </div>
51  );
52}

Zaawansowane przypadki użycia

1. Przekazywanie informacji o poprzedniej lokalizacji

Możemy używać location state do zapamiętania strony, z której użytkownik przybył:

1function SecureArea() {
2  const navigate = useNavigate();
3  const { user } = useAuth();
4
5  useEffect(() => {
6    if (!user) {
7      // Przekierowanie do logowania z informacją o bieżącej ścieżce
8      navigate('/login', {
9        state: { from: location.pathname },
10        replace: true
11      });
12    }
13  }, [user, navigate, location]);
14
15  return (
16    <div className="secure-area">
17      <h1>Bezpieczna Sekcja Statku Kosmicznego</h1>
18      {/* Zawartość bezpiecznej sekcji */}
19    </div>
20  );
21}
22
23// W komponencie logowania
24function Login() {
25  const location = useLocation();
26  const navigate = useNavigate();
27
28  // Pobieramy oryginalną ścieżkę, na którą użytkownik próbował się dostać
29  const from = location.state?.from || '/dashboard';
30
31  const handleLogin = async (credentials) => {
32    const success = await performLogin(credentials);
33
34    if (success) {
35      // Po zalogowaniu wracamy na oryginalną stronę
36      navigate(from, { replace: true });
37    }
38  };
39
40  // Reszta komponentu logowania...
41}

2. Przekazywanie danych formularza w wieloetapowym procesie

Innym zaawansowanym przypadkiem jest zachowanie danych formularza podczas nawigacji w wieloetapowym procesie:

1function SpaceshipConfigStep1() {
2  const [config, setConfig] = useState({
3    name: '',
4    type: 'exploration',
5    crew: 4
6  });
7
8  const navigate = useNavigate();
9
10  const handleNext = () => {
11    // Walidacja formularza...
12
13    // Przejście do następnego kroku z danymi
14    navigate('/config/step2', {
15      state: { config }
16    });
17  };
18
19  return (
20    <div className="config-form">
21      <h2>Konfiguracja Statku Kosmicznego - Krok 1</h2>
22      <form onSubmit={e => { e.preventDefault(); handleNext(); }}>
23        <div className="form-field">
24          <label htmlFor="name">Nazwa statku:</label>
25          <input
26            id="name"
27            value={config.name}
28            onChange={e => setConfig({...config, name: e.target.value})}
29            required
30          />
31        </div>
32
33        <div className="form-field">
34          <label htmlFor="type">Typ misji:</label>
35          <select
36            id="type"
37            value={config.type}
38            onChange={e => setConfig({...config, type: e.target.value})}
39          >
40            <option value="exploration">Eksploracja</option>
41            <option value="science">Badania naukowe</option>
42            <option value="mining">Wydobycie zasobów</option>
43          </select>
44        </div>
45
46        <div className="form-field">
47          <label htmlFor="crew">Liczba załogi:</label>
48          <input
49            id="crew"
50            type="number"
51            min="1"
52            max="10"
53            value={config.crew}
54            onChange={e => setConfig({...config, crew: parseInt(e.target.value)})}
55            required
56          />
57        </div>
58
59        <button type="submit">Dalej</button>
60      </form>
61    </div>
62  );
63}
64
65function SpaceshipConfigStep2() {
66  const location = useLocation();
67  const navigate = useNavigate();
68
69  // Pobieramy konfigurację z poprzedniego kroku
70  const { config: prevConfig } = location.state || { config: null };
71
72  // Jeśli nie mamy konfiguracji, przekierowujemy z powrotem
73  useEffect(() => {
74    if (!prevConfig) {
75      navigate('/config/step1', { replace: true });
76    }
77  }, [prevConfig, navigate]);
78
79  // Inicjalizujemy stan z danymi z poprzedniego kroku
80  const [config, setConfig] = useState({
81    ...prevConfig,
82    engines: 'ion',
83    fuelCapacity: 5000,
84    lifeSupport: 'advanced'
85  });
86
87  const handleFinish = () => {
88    // Kompletna konfiguracja z obu kroków
89    const finalConfig = {
90      ...config
91    };
92
93    // Finalizujemy konfigurację
94    navigate('/config/summary', {
95      state: { finalConfig }
96    });
97  };
98
99  if (!prevConfig) return null; // Nie renderuj nic, dopóki przekierowanie się nie wykona
100
101  return (
102    <div className="config-form">
103      <h2>Konfiguracja Statku Kosmicznego - Krok 2</h2>
104      <form onSubmit={e => { e.preventDefault(); handleFinish(); }}>
105        {/* Drugi zestaw pól formularza */}
106
107        <div className="form-actions">
108          <button
109            type="button"
110            onClick={() => navigate(-1)}
111          >
112            Wstecz
113          </button>
114          <button type="submit">Zakończ</button>
115        </div>
116      </form>
117    </div>
118  );
119}

Funkcja replace i historia przeglądarki

W przykładach używaliśmy parametru

replace: true
. Jest to ważna opcja, która wpływa na zachowanie historii przeglądarki:

  • Gdy
    replace: false
    (domyślne) - dodaje nową stronę do historii. Użytkownik może wrócić do poprzedniej strony przyciskiem "Wstecz".
  • Gdy
    replace: true
    - zastępuje bieżącą stronę w historii. Gdy użytkownik naciśnie przycisk "Wstecz", wróci do strony sprzed ostatniego przekierowania.
1// Dodaje nową stronę do historii przeglądarki
2navigate('/dashboard', { state: { data: 'example' } });
3
4// Zastępuje bieżącą stronę w historii
5navigate('/dashboard', {
6  state: { data: 'example' },
7  replace: true
8});

Opcja

replace
jest szczególnie przydatna w procesach logowania, gdzie po przekierowaniu użytkownika do strony logowania i z powrotem, nie chcemy, aby mógł wrócić do strony logowania za pomocą przycisku "Wstecz".

Ograniczenia i alternatywy

Location state ma pewne ograniczenia:

  1. Nie jest trwały - dane są tracone przy odświeżeniu strony
  2. Limitowana wielkość - nie powinniśmy przekazywać bardzo dużych obiektów
  3. Nie jest widoczny w URL - co może być zaletą (prywatność) lub wadą (nie można zapamiętać/udostępnić dokładnego stanu)

Alternatywne metody przekazywania danych między stronami:

  1. Parametry URL: Dane są widoczne w adresie URL

    1// Przekazywanie przez URL
    2navigate(`/planet/mars?atmosphere=thin&temperature=-60`);
    3
    4// Odczytywanie
    5const { search } = useLocation();
    6const queryParams = new URLSearchParams(search);
    7const atmosphere = queryParams.get('atmosphere'); // 'thin'
  2. Globalny stan aplikacji: Zarządzanie stanem za pomocą Context API, Redux, Zustand, itp.

    1// Zapisanie danych
    2dispatch(setCurrentPlanet({ name: 'Mars', atmosphere: 'thin' }));
    3navigate('/planet-details');
    4
    5// Odczytanie danych
    6const currentPlanet = useSelector(state => state.planets.current);
  3. Lokalne przechowywanie: sessionStorage, localStorage

    1// Zapisanie danych
    2sessionStorage.setItem('planetData', JSON.stringify({ name: 'Mars' }));
    3navigate('/planet-details');
    4
    5// Odczytanie danych
    6const planetData = JSON.parse(sessionStorage.getItem('planetData') || '{}');

Najlepsze praktyki

  1. Minimalny zakres danych: Przekazuj tylko niezbędne dane
  2. Walidacja danych: Zawsze sprawdzaj, czy dane istnieją przed ich użyciem
  3. Zapewniaj fallback: Miej plan B, gdy dane nie są dostępne
  4. Unikaj wrażliwych danych: Nie przekazuj poufnych informacji przez location state
  5. Używaj typesafe: Jeśli korzystasz z TypeScript, określaj typy dla danych w location state

Podsumowanie

Przekazywanie stanu przez lokalizacje w React Router jest jak międzygalaktyczna komunikacja - możemy wysyłać istotne dane między różnymi "stacjami" (stronami) naszej aplikacji bez konieczności zapamiętywania ich globalnie.

Ta technika jest szczególnie przydatna w przypadkach takich jak:

  • Przekazywanie szczegółów obiektu na stronę szczegółową
  • Zapamiętywanie oryginalnej lokalizacji przy przekierowaniach uwierzytelniania
  • Zachowywanie stanu formularza w procesach wieloetapowych
  • Przekazywanie wiadomości i komunikatów między stronami (np. "Operacja zakończona pomyślnie")

Wybierając między location state a innymi metodami przekazywania danych, zawsze rozważaj trwałość, widoczność i wielkość danych, aby wybrać najlepsze podejście dla konkretnego przypadku użycia.

Ir a CodeWorlds