W międzyplanetarnych podróżach, koordynaty i identyfikatory ciał niebieskich są kluczowe dla nawigacji. Podobnie w React Router, parametry URL pozwalają na precyzyjne kierowanie użytkownika do konkretnych zasobów aplikacji. W tym module poznamy, jak używać, odczytywać i manipulować parametrami URL w naszych kosmicznych aplikacjach.
Parametry URL to zmienne części adresu URL, które mogą zawierać dynamiczne wartości. W React Router występują w dwóch głównych formach:
/planets/:planetId)/planets?type=rocky&inhabited=true)Parametry ścieżki są częścią samej struktury URL i definiowane są przez dwukropek (
:) w definicji trasy.1import { Routes, Route } from 'react-router-dom';
2
3function AppRoutes() {
4 return (
5 <Routes>
6 <Route path="/" element={<Dashboard />} />
7 <Route path="/planets/:planetId" element={<PlanetDetails />} />
8 <Route path="/missions/:year/:missionId" element={<MissionDetails />} />
9 </Routes>
10 );
11}W powyższym przykładzie:
:planetId to parametr w ścieżce /planets/:planetId:year i :missionId to parametry w ścieżce /missions/:year/:missionIdTe ścieżki będą pasować do URL-i takich jak:
/planets/mars/planets/jupiter/missions/2150/mars-expeditionDo odczytania wartości parametrów ścieżki w komponencie używamy hooka
useParams:1import { useParams } from 'react-router-dom';
2
3function PlanetDetails() {
4 const { planetId } = useParams();
5
6 return (
7 <div className="planet-details">
8 <h1>Szczegóły planety: {planetId}</h1>
9 {/* Reszta komponentu używająca wartości planetId */}
10 </div>
11 );
12}W bardziej złożonym komponencie możemy użyć parametru do pobrania danych:
1import { useParams } from 'react-router-dom';
2import { useState, useEffect } from 'react';
3
4function PlanetDetails() {
5 const { planetId } = useParams();
6 const [planetData, setPlanetData] = useState(null);
7 const [loading, setLoading] = useState(true);
8 const [error, setError] = useState(null);
9
10 useEffect(() => {
11 // Resetujemy stan przy zmianie parametru
12 setLoading(true);
13 setError(null);
14
15 // Pobieramy dane dla konkretnej planety
16 fetch(`https://api.space-explorer.com/planets/${planetId}`)
17 .then(response => {
18 if (!response.ok) {
19 throw new Error('Nie można pobrać danych planety');
20 }
21 return response.json();
22 })
23 .then(data => {
24 setPlanetData(data);
25 setLoading(false);
26 })
27 .catch(err => {
28 setError(err.message);
29 setLoading(false);
30 });
31 }, [planetId]); // Ponowne wykonanie efektu przy zmianie planetId
32
33 if (loading) return <div>Ładowanie danych planety...</div>;
34 if (error) return <div>Błąd: {error}</div>;
35 if (!planetData) return <div>Nie znaleziono planety</div>;
36
37 return (
38 <div className="planet-details">
39 <h1>{planetData.name}</h1>
40 <div className="planet-info">
41 <p>Średnica: {planetData.diameter} km</p>
42 <p>Odległość od Słońca: {planetData.distanceFromSun} mln km</p>
43 <p>Temperatura powierzchni: {planetData.surfaceTemperature}°C</p>
44 <p>Typ: {planetData.type}</p>
45 </div>
46 {/* Więcej informacji o planecie */}
47 </div>
48 );
49}W React Router v6 nie ma bezpośredniego sposobu definiowania opcjonalnych parametrów ścieżki. Zamiast tego, możemy użyć wielu tras:
1<Routes>
2 <Route path="/missions" element={<MissionsList />} />
3 <Route path="/missions/:year" element={<MissionsList />} />
4</Routes>Lub wykorzystać parametry zapytania dla opcjonalnych wartości.
Parametry zapytania to pary klucz-wartość dodawane po znaku zapytania (
?) w URL.
Przykład: /search?query=mars&category=planetsHook
useSearchParams pozwala na odczytanie i manipulację parametrami zapytania:1import { useSearchParams } from 'react-router-dom';
2
3function PlanetSearch() {
4 // searchParams to obiekt URLSearchParams
5 // setSearchParams to funkcja do aktualizacji parametrów
6 const [searchParams, setSearchParams] = useSearchParams();
7
8 // Odczytanie wartości parametrów
9 const query = searchParams.get('query') || '';
10 const type = searchParams.get('type') || 'all';
11
12 // Funkcja obsługująca wyszukiwanie
13 const handleSearch = (e) => {
14 e.preventDefault();
15 const formData = new FormData(e.target);
16 const newQuery = formData.get('searchQuery');
17 const newType = formData.get('planetType');
18
19 // Aktualizacja parametrów zapytania
20 setSearchParams({ query: newQuery, type: newType });
21 };
22
23 return (
24 <div className="planet-search">
25 <h1>Wyszukiwarka planet</h1>
26
27 <form onSubmit={handleSearch}>
28 <input
29 name="searchQuery"
30 defaultValue={query}
31 placeholder="Wpisz nazwę planety..."
32 />
33
34 <select name="planetType" defaultValue={type}>
35 <option value="all">Wszystkie typy</option>
36 <option value="rocky">Skaliste</option>
37 <option value="gas-giant">Gazowe olbrzymy</option>
38 <option value="ice-giant">Lodowe olbrzymy</option>
39 </select>
40
41 <button type="submit">Szukaj</button>
42 </form>
43
44 <div className="search-results">
45 {/* Wyniki wyszukiwania w zależności od query i type */}
46 <p>Szukana fraza: {query || '(brak)'}</p>
47 <p>Typ planety: {type}</p>
48 </div>
49 </div>
50 );
51}Możemy manipulować parametrami zapytania w różny sposób:
1// Ustawienie nowych parametrów (zastępuje wszystkie istniejące)
2setSearchParams({ query: 'mars', type: 'rocky' });
3
4// Dodanie lub aktualizacja parametru bez usuwania istniejących
5setSearchParams(params => {
6 params.set('query', 'mars');
7 return params;
8});
9
10// Usunięcie parametru
11setSearchParams(params => {
12 params.delete('type');
13 return params;
14});
15
16// Sprawdzenie, czy parametr istnieje
17const hasType = searchParams.has('type');
18
19// Pobranie wszystkich wartości dla parametru (jeśli występuje wielokrotnie)
20const allCategories = searchParams.getAll('category');/planets/mars)Możemy łączyć oba rodzaje parametrów dla większej elastyczności:
1// URL: /planets/mars?tab=geology&unit=metric
2
3import { useParams, useSearchParams } from 'react-router-dom';
4
5function PlanetDetails() {
6 const { planetId } = useParams();
7 const [searchParams] = useSearchParams();
8
9 const activeTab = searchParams.get('tab') || 'overview';
10 const unit = searchParams.get('unit') || 'metric';
11
12 return (
13 <div>
14 <h1>Planeta: {planetId}</h1>
15
16 <div className="tabs">
17 <TabLink to={`/planets/${planetId}?tab=overview&unit=${unit}`}
18 active={activeTab === 'overview'}>
19 Przegląd
20 </TabLink>
21 <TabLink to={`/planets/${planetId}?tab=geology&unit=${unit}`}
22 active={activeTab === 'geology'}>
23 Geologia
24 </TabLink>
25 <TabLink to={`/planets/${planetId}?tab=atmosphere&unit=${unit}`}
26 active={activeTab === 'atmosphere'}>
27 Atmosfera
28 </TabLink>
29 </div>
30
31 <div className="unit-switcher">
32 <UnitToggle
33 metric={unit === 'metric'}
34 onChange={(isMetric) => {
35 const newUnit = isMetric ? 'metric' : 'imperial';
36 // Aktualizacja tylko parametru unit, zachowanie tab
37 searchParams.set('unit', newUnit);
38 setSearchParams(searchParams);
39 }}
40 />
41 </div>
42
43 {/* Zawartość zależna od activeTab i unit */}
44 </div>
45 );
46}Funkcja
createSearchParams pomaga w tworzeniu parametrów zapytania z obiektów JavaScript:1import { createSearchParams, useNavigate } from 'react-router-dom';
2
3function PlanetFilterPanel() {
4 const navigate = useNavigate();
5
6 const applyFilters = (filters) => {
7 // Konwersja obiektu filtrów na parametry URL
8 const params = createSearchParams({
9 type: filters.type,
10 minSize: filters.minSize,
11 hasWater: filters.hasWater ? 'true' : undefined,
12 sortBy: filters.sortBy
13 });
14
15 // Nawigacja z parametrami
16 navigate({
17 pathname: '/planets',
18 search: `?${params}`
19 });
20 };
21
22 return (
23 <form onSubmit={(e) => {
24 e.preventDefault();
25 const form = e.target;
26 applyFilters({
27 type: form.type.value,
28 minSize: form.minSize.value,
29 hasWater: form.hasWater.checked,
30 sortBy: form.sortBy.value
31 });
32 }}>
33 {/* Formularz filtrowania */}
34 <button type="submit">Zastosuj filtry</button>
35 </form>
36 );
37}Tworząc linki z parametrami, możemy używać:
1// Proste łączenie ciągów
2<Link to={`/planets/${planetId}`}>
3 Szczegóły planety {planetName}
4</Link>
5
6// Użycie obiektu (bardziej elastyczne)
7<Link to={{
8 pathname: `/planets/${planetId}`,
9}}>
10 Szczegóły planety {planetName}
11</Link>1import { Link, createSearchParams } from 'react-router-dom';
2
3// Z użyciem createSearchParams
4<Link to={{
5 pathname: '/planets',
6 search: `?${createSearchParams({
7 type: 'rocky',
8 inhabited: 'true'
9 })}`
10}}>
11 Zamieszkane planety skaliste
12</Link>
13
14// Lub ręczne tworzenie parametrów
15<Link to={`/planets?type=rocky&inhabited=true`}>
16 Zamieszkane planety skaliste
17</Link>Zobaczmy teraz kompletny przykład kosmicznej aplikacji wykorzystującej zarówno parametry ścieżki, jak i parametry zapytania:
1import React, { useState, useEffect } from 'react';
2import {
3 BrowserRouter,
4 Routes,
5 Route,
6 Link,
7 useParams,
8 useSearchParams,
9 createSearchParams,
10 useNavigate
11} from 'react-router-dom';
12
13// Symulacja API
14const planetsData = [
15 { id: 'mercury', name: 'Merkury', type: 'rocky', diameter: 4879, hasWater: false },
16 { id: 'venus', name: 'Wenus', type: 'rocky', diameter: 12104, hasWater: false },
17 { id: 'earth', name: 'Ziemia', type: 'rocky', diameter: 12742, hasWater: true },
18 { id: 'mars', name: 'Mars', type: 'rocky', diameter: 6779, hasWater: true },
19 { id: 'jupiter', name: 'Jowisz', type: 'gas-giant', diameter: 139820, hasWater: true },
20 { id: 'saturn', name: 'Saturn', type: 'gas-giant', diameter: 116460, hasWater: true },
21 { id: 'uranus', name: 'Uran', type: 'ice-giant', diameter: 50724, hasWater: true },
22 { id: 'neptune', name: 'Neptun', type: 'ice-giant', diameter: 49244, hasWater: true },
23];
24
25// Komponenty główne
26function PlanetsList() {
27 const [searchParams, setSearchParams] = useSearchParams();
28 const navigate = useNavigate();
29
30 // Odczytanie parametrów zapytania
31 const type = searchParams.get('type') || 'all';
32 const hasWater = searchParams.get('hasWater');
33 const sortBy = searchParams.get('sortBy') || 'name';
34
35 // Filtrowanie planet
36 const filteredPlanets = planetsData
37 .filter(planet => type === 'all' || planet.type === type)
38 .filter(planet => hasWater === null || (hasWater === 'true' && planet.hasWater))
39 .sort((a, b) => {
40 if (sortBy === 'name') return a.name.localeCompare(b.name);
41 if (sortBy === 'diameter') return b.diameter - a.diameter;
42 return 0;
43 });
44
45 // Funkcja aktualizująca filtry
46 const updateFilters = (newFilters) => {
47 const params = createSearchParams({
48 ...Object.fromEntries(searchParams),
49 ...newFilters
50 });
51 setSearchParams(params);
52 };
53
54 return (
55 <div className="planets-list">
56 <h1>Katalog planet</h1>
57
58 <div className="filters">
59 <label>
60 Typ planety:
61 <select
62 value={type}
63 onChange={(e) => updateFilters({ type: e.target.value })}
64 >
65 <option value="all">Wszystkie</option>
66 <option value="rocky">Skaliste</option>
67 <option value="gas-giant">Gazowe olbrzymy</option>
68 <option value="ice-giant">Lodowe olbrzymy</option>
69 </select>
70 </label>
71
72 <label>
73 <input
74 type="checkbox"
75 checked={hasWater === 'true'}
76 onChange={(e) => updateFilters({ hasWater: e.target.checked ? 'true' : null })}
77 />
78 Tylko z wodą
79 </label>
80
81 <label>
82 Sortuj wg:
83 <select
84 value={sortBy}
85 onChange={(e) => updateFilters({ sortBy: e.target.value })}
86 >
87 <option value="name">Nazwa</option>
88 <option value="diameter">Rozmiar</option>
89 </select>
90 </label>
91 </div>
92
93 <ul className="planets-grid">
94 {filteredPlanets.map(planet => (
95 <li key={planet.id} className={`planet ${planet.type}`}>
96 <Link to={`/planets/${planet.id}`}>
97 <h2>{planet.name}</h2>
98 <p>Typ: {planet.type}</p>
99 <p>Średnica: {planet.diameter} km</p>
100 <p>Woda: {planet.hasWater ? 'Obecna' : 'Brak'}</p>
101 </Link>
102 </li>
103 ))}
104 </ul>
105
106 {filteredPlanets.length === 0 && (
107 <p>Nie znaleziono planet spełniających kryteria.</p>
108 )}
109
110 <button onClick={() => navigate('/')}>
111 Powrót do centrum kontroli
112 </button>
113 </div>
114 );
115}
116
117function PlanetDetails() {
118 const { planetId } = useParams();
119 const [searchParams, setSearchParams] = useSearchParams();
120 const navigate = useNavigate();
121
122 // Odczytanie parametrów zapytania
123 const activeTab = searchParams.get('tab') || 'overview';
124
125 // Znalezienie planety o danym ID
126 const planet = planetsData.find(p => p.id === planetId);
127
128 // Jeśli nie znaleziono planety
129 if (!planet) {
130 return (
131 <div className="planet-not-found">
132 <h1>Planeta nie istnieje!</h1>
133 <p>Nie znaleziono planety o identyfikatorze: {planetId}</p>
134 <button onClick={() => navigate('/planets')}>
135 Powrót do katalogu planet
136 </button>
137 </div>
138 );
139 }
140
141 // Zmiana aktywnej zakładki
142 const changeTab = (tab) => {
143 setSearchParams({ tab });
144 };
145
146 return (
147 <div className="planet-details">
148 <h1>{planet.name}</h1>
149
150 <div className="tabs">
151 <button
152 className={activeTab === 'overview' ? 'active' : ''}
153 onClick={() => changeTab('overview')}
154 >
155 Przegląd
156 </button>
157 <button
158 className={activeTab === 'composition' ? 'active' : ''}
159 onClick={() => changeTab('composition')}
160 >
161 Skład
162 </button>
163 <button
164 className={activeTab === 'exploration' ? 'active' : ''}
165 onClick={() => changeTab('exploration')}
166 >
167 Eksploracja
168 </button>
169 </div>
170
171 <div className="tab-content">
172 {activeTab === 'overview' && (
173 <div>
174 <h2>Przegląd planety {planet.name}</h2>
175 <p>Typ: {planet.type}</p>
176 <p>Średnica: {planet.diameter} km</p>
177 <p>Woda: {planet.hasWater ? 'Obecna' : 'Brak'}</p>
178 </div>
179 )}
180
181 {activeTab === 'composition' && (
182 <div>
183 <h2>Skład planety {planet.name}</h2>
184 <p>Tu będą informacje o składzie planety...</p>
185 </div>
186 )}
187
188 {activeTab === 'exploration' && (
189 <div>
190 <h2>Eksploracja planety {planet.name}</h2>
191 <p>Tu będą informacje o misjach eksploracyjnych...</p>
192 </div>
193 )}
194 </div>
195
196 <Link to="/planets">Powrót do katalogu planet</Link>
197 </div>
198 );
199}
200
201function App() {
202 return (
203 <BrowserRouter>
204 <div className="space-app">
205 <header>
206 <h1>Kosmiczny System Nawigacyjny</h1>
207 </header>
208
209 <main>
210 <Routes>
211 <Route path="/" element={<Dashboard />} />
212 <Route path="/planets" element={<PlanetsList />} />
213 <Route path="/planets/:planetId" element={<PlanetDetails />} />
214 <Route path="*" element={<NotFound />} />
215 </Routes>
216 </main>
217
218 <footer>
219 <p>© 2150 Międzyplanetarna Agencja Kosmiczna</p>
220 </footer>
221 </div>
222 </BrowserRouter>
223 );
224}
225
226// Pozostałe komponenty
227function Dashboard() {
228 return (
229 <div className="dashboard">
230 <h1>Centrum Kontroli Misji</h1>
231 <p>Witaj w systemie nawigacji kosmicznej!</p>
232 <Link to="/planets">Przeglądaj katalog planet</Link>
233 </div>
234 );
235}
236
237function NotFound() {
238 return (
239 <div className="not-found">
240 <h1>Błąd nawigacji: 404</h1>
241 <p>Zagubiony w kosmosie! Ta strona nie istnieje.</p>
242 <Link to="/">Powrót do Centrum Kontroli</Link>
243 </div>
244 );
245}
246
247export default App;Parametry URL są potężnym narzędziem w nawigacji kosmicznej (i React Router). Pozwalają na:
Dzięki nim, użytkownicy mogą precyzyjnie nawigować do konkretnych lokalizacji w kosmicznej aplikacji, a ty jako programista możesz tworzyć interaktywne i przyjazne dla użytkownika interfejsy.