Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Parametry URL w kosmicznej nawigacji

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.

Czym są parametry URL?

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:

  1. Parametry ścieżki (path parameters) - wbudowane w strukturę URL (np.
    /planets/:planetId
    )
  2. Parametry zapytania (query parameters) - dodawane po znaku zapytania (np.
    /planets?type=rocky&inhabited=true
    )

Parametry ścieżki (Path Parameters)

Parametry ścieżki są częścią samej struktury URL i definiowane są przez dwukropek (

:
) w definicji trasy.

Definiowanie tras z parametrami ścieżki

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/:missionId

Te ścieżki będą pasować do URL-i takich jak:

  • /planets/mars
  • /planets/jupiter
  • /missions/2150/mars-expedition

Dostęp do parametrów ścieżki za pomocą useParams

Do 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}

Opcjonalne parametry ścieżki

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 (Query Parameters)

Parametry zapytania to pary klucz-wartość dodawane po znaku zapytania (

?
) w URL. Przykład:
/search?query=mars&category=planets

Dostęp do parametrów zapytania za pomocą useSearchParams

Hook

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}

Manipulowanie parametrami zapytania

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');

Parametry zapytania vs. parametry ścieżki - kiedy używać których?

Parametry ścieżki:

  • Do identyfikacji głównego zasobu (np.
    /planets/mars
    )
  • Gdy parametr jest obowiązkowy
  • Gdy struktura URL powinna odzwierciedlać hierarchię zasobów
  • Przykłady: ID planety, rok misji, nazwa gwiazdozbioru

Parametry zapytania:

  • Do filtrowania, sortowania, paginacji
  • Dla parametrów opcjonalnych
  • Gdy mamy wiele kombinacji parametrów
  • Przykłady: kryteria wyszukiwania, strona wyników, limity, filtry

Łączenie parametrów ścieżki i zapytania

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}

createSearchParams - ułatwienie tworzenia parametrów zapytania

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}

Generowanie linków z parametrami

Tworząc linki z parametrami, możemy używać:

Dla parametrów ścieżki:

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>

Dla parametrów zapytania:

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>

Pełny przykład aplikacji z parametrami URL

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>&copy; 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;

Najlepsze praktyki dla parametrów URL

  1. Używaj czytelnych nazw parametrów - powinny opisywać, co reprezentują
  2. Wybieraj odpowiedni typ parametrów - parametry ścieżki dla głównych zasobów, parametry zapytania dla filtrów
  3. Obsługuj brakujące parametry - zawsze zapewniaj wartości domyślne
  4. Waliduj parametry - sprawdzaj, czy wartości są poprawne przed użyciem
  5. Zachowuj stan parametrów zapytania - aktualizuj tylko te parametry, które się zmieniają
  6. Używaj parametrów do przechowywania stanu UI - ułatwia to udostępnianie i zapisywanie stanu

Podsumowanie

Parametry URL są potężnym narzędziem w nawigacji kosmicznej (i React Router). Pozwalają na:

  • Identyfikację konkretnych zasobów (parametry ścieżki)
  • Filtrowanie i sortowanie danych (parametry zapytania)
  • Zapamiętywanie stanu interfejsu użytkownika
  • Tworzenie udostępnialnych i zakładkowalnych linków
  • Obsługę historii przeglądarki

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.

Ir a CodeWorlds