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.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.
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.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}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}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}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}W przykładach używaliśmy parametru
replace: true. Jest to ważna opcja, która wpływa na zachowanie historii przeglądarki:replace: false (domyślne) - dodaje nową stronę do historii. Użytkownik może wrócić do poprzedniej strony przyciskiem "Wstecz".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".Location state ma pewne ograniczenia:
Alternatywne metody przekazywania danych między stronami:
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'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);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') || '{}');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:
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.