Kurs JavaScript i React · Moduł 6: React Router
Przekierowania w kosmicznej nawigacji
W tej lekcji8
Wyobraź sobie że stacja kosmiczna zmieniła adres: dawna ścieżka /old-station prowadzi donikąd, a załoga wciąż ma ją w zakładkach. Albo niezalogowany turysta próbuje wejść do centrum dowodzenia. W obu przypadkach nie chcemy pokazywać pustej strony ani błędu. Chcemy, żeby statek sam zmienił kurs.
W przestrzeni międzygwiezdnej czasami musimy natychmiast przekierować nasz statek na inną trasę. W świecie React Router, przekierowania działają podobnie - pozwalają nam przenieść użytkownika z jednej ścieżki na drugą automatycznie.
Przykłady w tej lekcji działają tak samo w React Router v6 i v7. W v7 te same komponenty i hooki możesz importować z pakietu react-router, a react-router-dom nadal je udostępnia, więc importy z tej lekcji pozostają poprawne. W wersji v8, wydanej w czerwcu 2026, pakietu react-router-dom już nie ma: te same komponenty i hooki importujesz wtedy z react-router, a sam kod przykładów się nie zmienia.
Komponent Navigate
Najprostszym sposobem na utworzenie przekierowania jest użycie komponentu Navigate z React Router:
1import { Navigate } from 'react-router-dom';
2
3function RedirectToControl() {
4 return <Navigate to="/control-center" />;
5}Gdy ten komponent jest renderowany, użytkownik zostanie automatycznie przekierowany na wskazaną ścieżkę. Jest to deklaratywny sposób na przekierowanie - po prostu umieszczasz komponent w drzewie JSX.
Przekierowanie starej trasy
Najprostszy scenariusz to trasa, która zmieniła adres. Konfiguracja przebiega w czterech krokach: definiujesz trasę źródłową, jako jej element podajesz Navigate z nowym adresem, dodajesz replace, a React Router sam przenosi użytkownika:
1<Routes>
2 <Route path="/old-station" element={<Navigate to="/space-station" replace />} />
3 <Route path="/space-station" element={<SpaceStation />} />
4</Routes>Kto wpisze stary adres, trafi na /space-station, a w historii przeglądarki nie zostanie ślad po starej ścieżce. Komponent SpaceStation nie wie nic o przekierowaniu i nie wymaga żadnych zmian.
Przekierowania warunkowe
Często potrzebujemy przekierować użytkownika tylko w określonych warunkach, na przykład gdy nie jest zalogowany. Hook useAuth w przykładach to nasz własny hook (nie część React Router), który zwraca informacje o zalogowanym użytkowniku:
1function SecretMoonBase() {
2 const isAstronaut = useAuth();
3
4 if (!isAstronaut) {
5 return <Navigate to="/access-denied" replace />;
6 }
7
8 return (
9 <div>
10 <h1>Tajna baza księżycowa</h1>
11 <p>Witaj astronauto! Masz dostęp do tajnych danych.</p>
12 </div>
13 );
14}Atrybut replace sprawia, że przekierowanie zastępuje bieżącą stronę w historii przeglądarki, zamiast dodawać nową. Dzięki temu użytkownik nie trafi z powrotem na chronioną stronę po kliknięciu przycisku "wstecz".
Przekierowania programowe
Czasami potrzebujemy przekierować użytkownika w odpowiedzi na jakieś zdarzenie, jak przesłanie formularza. Używamy wtedy hooka useNavigate:
1import { useNavigate } from 'react-router-dom';
2
3function LaunchControlForm() {
4 const navigate = useNavigate();
5
6 const handleLaunch = (event) => {
7 event.preventDefault();
8 // Logika przygotowania startu...
9
10 // Przekierowanie do strony odliczania
11 navigate('/countdown');
12
13 // Lub z parametrem replace
14 // navigate('/countdown', { replace: true });
15 };
16
17 return (
18 <form onSubmit={handleLaunch}>
19 <h2>Panel kontroli startu</h2>
20 <button type="submit">Rozpocznij odliczanie</button>
21 </form>
22 );
23}Funkcję navigate wywołujemy w obsłudze zdarzenia, a nie bezpośrednio w ciele komponentu. React Router ostrzega, jeśli wywołasz ją w trakcie pierwszego renderowania. Tam, gdzie decyzja zapada podczas renderowania, lepiej pasuje komponent Navigate.
Te dwa narzędzia to końce jednej skali. Od najbardziej deklaratywnego do imperatywnego: Link (zwykły odnośnik w JSX), NavLink (odnośnik ze stanem aktywnym), Navigate (przekierowanie w JSX) i useNavigate (hook wywoływany w kodzie).
Obsługa nieistniejących ścieżek (404)
React Router pozwala nam łatwo obsłużyć przypadek, gdy użytkownik próbuje odwiedzić ścieżkę, która nie istnieje:
1<Routes>
2 <Route path="/mission-control" element={<MissionControl />} />
3 <Route path="/navigation" element={<Navigation />} />
4 <Route path="*" element={<Navigate to="/lost-in-space" />} />
5</Routes>Ścieżka * dopasowuje się do każdej trasy, która nie pasuje do żadnej z poprzednich. Zamiast Navigate możemy również wyrenderować komponent 404:
1<Route path="*" element={<LostInSpace />} />Polecam drugi wariant. Strona "zagubiony w kosmosie" zostawia w pasku adresu błędny URL, więc użytkownik widzi, co wpisał, i może to poprawić.
Przekierowania z parametrami
Możemy również przekazać dodatkowe dane podczas przekierowania. Służy do tego opcja state, dostępna zarówno w komponencie, jak i w funkcji navigate:
1// Przekierowanie deklaratywne z parametrami
2<Navigate
3 to="/asteroid-alert"
4 state={{ severity: "high", location: "sector-7" }}
5/>
6
7// Przekierowanie programowe z parametrami
8navigate('/asteroid-alert', {
9 state: { severity: "high", location: "sector-7" }
10});Dane ze state nie trafiają do adresu URL, więc adres pozostaje czysty. W komponencie docelowym możemy odczytać te dane za pomocą hooka useLocation:
1import { useLocation } from 'react-router-dom';
2
3function AsteroidAlert() {
4 const location = useLocation();
5 const { severity, location: asteroidLocation } = location.state || {};
6
7 return (
8 <div className="alert">
9 <h1>Ostrzeżenie o asteroidzie!</h1>
10 {severity && asteroidLocation && (
11 <p>
12 Wykryto asteroidę o zagrożeniu {severity} w lokalizacji {asteroidLocation}.
13 </p>
14 )}
15 </div>
16 );
17}Zwróć uwagę na location.state || {}. Jeśli ktoś wejdzie na stronę bezpośrednio z paska adresu, state będzie puste, a zabezpieczenie chroni komponent przed błędem.
Przekierowania z uwierzytelnianiem
Najczęstszym przypadkiem użycia przekierowań jest ochrona tras przed nieuprawnionymi użytkownikami. Komponent ProtectedRoute odczytuje bieżącą lokalizację hookiem useLocation, żeby zapamiętać, dokąd użytkownik chciał lecieć:
1function ProtectedRoute({ children }) {
2 const { user, loading } = useAuth();
3 const location = useLocation();
4
5 if (loading) {
6 return <LoadingSpinner />;
7 }
8
9 if (!user) {
10 // Przekierowanie do strony logowania z zapisaniem oryginalnej ścieżki
11 return <Navigate to="/login" state={{ from: location.pathname }} />;
12 }
13
14 return children;
15}
16
17// Użycie:
18<Routes>
19 <Route path="/mission-control" element={
20 <ProtectedRoute>
21 <MissionControl />
22 </ProtectedRoute>
23 } />
24</Routes>Chroniony komponent MissionControl pozostaje niezmieniony, całą logikę dostępu trzyma opakowanie. Na stronie logowania możemy odczytać oryginalną ścieżkę i przekierować z powrotem po zalogowaniu:
1function Login() {
2 const navigate = useNavigate();
3 const location = useLocation();
4 const from = location.state?.from || '/dashboard';
5
6 const handleLogin = async (credentials) => {
7 const success = await loginUser(credentials);
8
9 if (success) {
10 // Przekierowanie z powrotem do oryginalnej strony
11 navigate(from, { replace: true });
12 }
13 };
14
15 // Reszta komponentu...
16}Tutaj replace: true usuwa stronę logowania z historii, więc przycisk "wstecz" nie cofnie zalogowanego astronauty do formularza.
W trybie danych React Router (od v6.4, także w v7) przekierowania wykonuje się często już w funkcjach loader i action za pomocą funkcji redirect, zanim komponent w ogóle się wyrenderuje. Poznasz to przy bardziej zaawansowanej architekturze routingu.
Podsumowanie
Przekierowania są potężnym narzędziem w aplikacjach React wykorzystujących React Router. Pozwalają na:
- Automatyczne przenoszenie użytkowników między ścieżkami
- Tworzenie chronionych stref wymagających uwierzytelnienia
- Obsługę nieistniejących ścieżek (404)
- Przekazywanie danych między stronami podczas przekierowania
Pamiętaj, aby używać parametru replace gdy chcesz zapobiec powrotowi użytkownika do poprzedniej strony za pomocą przycisku "wstecz" - jest to szczególnie ważne w przypadku stron logowania i innych scenariuszy uwierzytelniania.
Z zabezpieczaniem ścieżek zetknąłeś się już w poprzedniej lekcji, a w następnej zobaczysz, jak lazy loading ładuje moduły statku dopiero wtedy, gdy są potrzebne.
Zapamiętaj: Navigate zmienia kurs podczas renderowania, useNavigate po zdarzeniu, a replace sprząta ślady w dzienniku lotu.
Kod do tej lekcji: App.jsx
1import React from 'react';
2import { BrowserRouter, Routes, Route, Link, Outlet, useParams } from 'react-router-dom';
3
4// Layout dla sekcji Fleet
5function FleetLayout() {
6 return (
7 <div className="fleet-layout">
8 <aside className="fleet-nav">
9 <h2>Flota</h2>
10 <Link to="/fleet/alpha">Alpha Squadron</Link>
11 <Link to="/fleet/beta">Beta Squadron</Link>
12 <Link to="/fleet/gamma">Gamma Squadron</Link>
13 </aside>
14 <main className="fleet-content">
15 {/* Outlet renderuje zagnieżdżone trasy */}
16 <Outlet />
17 </main>
18 </div>
19 );
20}
21
22// Strona główna floty
23function FleetHome() {
24 return (
25 <div className="page">
26 <h1>Wybierz eskadron</h1>
27 <p>Kliknij na eskadron w menu, aby zobaczyć szczegóły.</p>
28 </div>
29 );
30}
31
32// Szczegóły eskadronu
33function SquadronDetails() {
34 const { squadronId } = useParams();
35
36 return (
37 <div className="page squadron">
38 <h1>Eskadron: {squadronId?.toUpperCase()}</h1>
39
40 <div className="squadron-nav">
41 <Link to={`/fleet/${squadronId}/ships`}>Statki</Link>
42 <Link to={`/fleet/${squadronId}/crew`}>Załoga</Link>
43 <Link to={`/fleet/${squadronId}/missions`}>Misje</Link>
44 </div>
45
46 {/* Kolejny Outlet dla głębiej zagnieżdżonych tras */}
47 <Outlet />
48 </div>
49 );
50}
51
52// Komponenty zagnieżdżone dla eskadronu
53function SquadronShips() {
54 const { squadronId } = useParams();
55 return (
56 <div className="sub-page">
57 <h2>Statki eskadronu {squadronId}</h2>
58 <ul>
59 <li>Fighter-01</li>
60 <li>Fighter-02</li>
61 <li>Cruiser-01</li>
62 </ul>
63 </div>
64 );
65}
66
67function SquadronCrew() {
68 const { squadronId } = useParams();
69 return (
70 <div className="sub-page">
71 <h2>Załoga eskadronu {squadronId}</h2>
72 <ul>
73 <li>Kapitan: John Smith</li>
74 <li>Pilot: Anna Kowalska</li>
75 <li>Inżynier: Mark Johnson</li>
76 </ul>
77 </div>
78 );
79}
80
81function SquadronMissions() {
82 const { squadronId } = useParams();
83 return (
84 <div className="sub-page">
85 <h2>Misje eskadronu {squadronId}</h2>
86 <ul>
87 <li>Patrol sektora 7 - W trakcie</li>
88 <li>Eskort transportowca - Zakończona</li>
89 </ul>
90 </div>
91 );
92}
93
94function App() {
95 return (
96 <BrowserRouter>
97 <div className="app">
98 <header>
99 <Link to="/">Home</Link>
100 <Link to="/fleet">Flota</Link>
101 </header>
102
103 <Routes>
104 <Route path="/" element={<div className="page home"><h1>Space Command</h1></div>} />
105
106 {/* Zagnieżdżone trasy */}
107 <Route path="/fleet" element={<FleetLayout />}>
108 <Route index element={<FleetHome />} />
109 <Route path=":squadronId" element={<SquadronDetails />}>
110 <Route path="ships" element={<SquadronShips />} />
111 <Route path="crew" element={<SquadronCrew />} />
112 <Route path="missions" element={<SquadronMissions />} />
113 </Route>
114 </Route>
115 </Routes>
116 </div>
117 </BrowserRouter>
118 );
119}
120
121export default App;Widzisz błąd w tej lekcji?
Zadania praktyczne w grze
- Układanie w pionie
Uporządkuj kroki konfiguracji przekierowania (redirect) w React Router v6:
- Układanie w pionie
Uporządkuj proces renderowania zagnieżdżonej trasy w React Router:
- Edytor kodu
Dynamic data loading z params
- Klikanie w kolejności
Ułóż kod programowej nawigacji po logowaniu:
- Układanie w pionie
Ułóż sposoby nawigacji React Router od deklaratywnego do imperatywnego: