Kurs JavaScript i React · Moduł 6: React Router

Przekierowania w kosmicznej nawigacji

6 min czytania
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:

  1. Automatyczne przenoszenie użytkowników między ścieżkami
  2. Tworzenie chronionych stref wymagających uwierzytelnienia
  3. Obsługę nieistniejących ścieżek (404)
  4. 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:

Przydatne artykuły