Kurs JavaScript i React · Moduł 6: React Router

Konfiguracja routingu w aplikacjach kosmicznych

7 min czytania
W tej lekcji8

W poprzednim module poznaliśmy podstawy React Router jako systemu nawigacji międzyplanetarnej. Teraz zagłębimy się w szczegóły konfiguracji routingu w różnych rodzajach aplikacji kosmicznych, zarówno tych małych jak i złożonych wielomodułowych systemach.

Różne typy routerów w React Router

React Router dostarcza kilka typów routerów, które możemy wykorzystać w zależności od środowiska, w którym działa nasza aplikacja:

1. BrowserRouter

BrowserRouter wykorzystuje HTML5 History API i jest najczęściej używanym routerem w nowoczesnych aplikacjach internetowych. Tworzy czyste, ładne URL-e bez hashtagu (#):

1import { BrowserRouter } from 'react-router-dom';
2
3function App() {
4  return (
5    <BrowserRouter>
6      {/* Komponenty routingu */}
7    </BrowserRouter>
8  );
9}

Ważne: BrowserRouter wymaga odpowiedniej konfiguracji serwera, który musi przekierowywać wszystkie żądania do pliku index.html (aby obsłużyć odświeżanie strony i bezpośrednie wejście na podstrony).

2. HashRouter

HashRouter używa funkcji hash w URL (część po #). Jest to prostsze rozwiązanie, które nie wymaga konfiguracji serwera, ale tworzy mniej eleganckie URL-e:

1import { HashRouter } from 'react-router-dom';
2
3function App() {
4  return (
5    <HashRouter>
6      {/* Komponenty routingu */}
7    </HashRouter>
8  );
9}

Przykładowe URL-e przy użyciu HashRouter: /#/mars, /#/jupiter

3. MemoryRouter

MemoryRouter przechowuje historię nawigacji w pamięci (nie używa URL przeglądarki). Jest przydatny do testów i aplikacji działających w środowiskach bez przeglądarki:

1import { MemoryRouter } from 'react-router-dom';
2
3function App() {
4  return (
5    <MemoryRouter initialEntries={['/mars', '/jupiter']} initialIndex={1}>
6      {/* Komponenty routingu */}
7    </MemoryRouter>
8  );
9}

Zaawansowana konfiguracja Route

Komponent Route oferuje więcej opcji niż tylko podstawowe mapowanie ścieżki do elementu:

1. Ścieżki dynamiczne z parametrami

1<Route path="/planet/:planetId" element={<PlanetDetails />} />

Ta ścieżka pasuje do URL-i jak /planet/mars, /planet/jupiter, itd. Parametr :planetId jest dynamiczny i może być odczytany w komponencie.

2. Ścieżki opcjonalne

1<Route path="/missions/:year?/:month?" element={<MissionsList />} />

Znaki zapytania oznaczają parametry opcjonalne. Ta ścieżka pasuje do URL-i:

  • /missions (bez parametrów)
  • /missions/2150 (tylko rok)
  • /missions/2150/06 (rok i miesiąc)

3. Parametry z gwiazdką (catch-all)

1<Route path="/files/*" element={<FileExplorer />} />

Gwiazdka łapie wszystko po /files/, np. /files/documents/reports/annual.pdf.

Struktura routingu dla większych aplikacji kosmicznych

W obszernych aplikacjach kosmicznych, routing może stać się skomplikowany. Oto jak można go zorganizować:

1. Zagnieżdżone trasy

Możemy zagnieżdżać trasy, aby stworzyć hierarchiczną strukturę:

1function AppRoutes() {
2  return (
3    <Routes>
4      <Route path="/" element={<SpaceLayout />}>
5        <Route index element={<Dashboard />} />
6        <Route path="planets" element={<PlanetLayout />}>
7          <Route index element={<PlanetsList />} />
8          <Route path=":planetId" element={<PlanetDetails />} />
9        </Route>
10        <Route path="missions" element={<MissionLayout />}>
11          <Route index element={<MissionsDashboard />} />
12          <Route path="scheduled" element={<ScheduledMissions />} />
13          <Route path="completed" element={<CompletedMissions />} />
14        </Route>
15      </Route>
16      <Route path="/auth" element={<AuthLayout />}>
17        <Route path="login" element={<Login />} />
18        <Route path="register" element={<Register />} />
19      </Route>
20      <Route path="*" element={<NotFound />} />
21    </Routes>
22  );
23}

W powyższym przykładzie:

  • SpaceLayout jest głównym layoutem dla ścieżek zaczynających się od "/"
  • PlanetLayout jest layoutem dla wszystkich ścieżek z sekcji "planets"
  • Element index określa, co ma być renderowane dla ścieżki nadrzędnej (np. "/planets")

2. Route element vs. component

Od React Router v6 używamy propsa element zamiast component z wcześniejszych wersji:

1// React Router v6 i nowsze (v7, v8)
2<Route path="/mars" element={<MarsBase />} />
3
4// React Router v5 (starszy)
5<Route path="/mars" component={MarsBase} />

Dlaczego to ważne? Syntax element pozwala na przekazanie wyrenderowanego elementu, co daje większą elastyczność:

1<Route
2  path="/mars"
3  element={
4    <MarsBase
5      temperature={-60}
6      oxygenLevel="low"
7      waterAvailable={true}
8    />
9  }
10/>

3. Outlet - wyświetlanie komponentów podścieżek

Komponent Outlet określa, gdzie w komponencie nadrzędnym mają być renderowane komponenty podścieżek:

1import { Outlet } from 'react-router-dom';
2
3function SpaceLayout() {
4  return (
5    <div className="space-layout">
6      <header>
7        <h1>Międzyplanetarny System Nawigacji</h1>
8        <nav>{/* Linki nawigacyjne */}</nav>
9      </header>
10
11      <main>
12        {/* Tutaj będą renderowane komponenty podścieżek */}
13        <Outlet />
14      </main>
15
16      <footer>
17        <p>Kosmiczne Centrum Dowodzenia &copy; 2150</p>
18      </footer>
19    </div>
20  );
21}

Konfiguracja z uwzględnieniem obsługi błędów

Dobrą praktyką jest dodanie globalnej obsługi błędów w naszym systemie routingu:

1function AppRoutes() {
2  return (
3    <Routes>
4      <Route path="/" element={<SpaceLayout />} errorElement={<GlobalError />}>
5        {/* Inne trasy */}
6      </Route>
7    </Routes>
8  );
9}
10
11function GlobalError() {
12  const error = useRouteError();
13
14  return (
15    <div className="error-container">
16      <h1>Houston, mamy problem!</h1>
17      <p>Wystąpił błąd w systemie nawigacji:</p>
18      <pre>{error.message || "Nieznany błąd"}</pre>
19      <button onClick={() => window.location.href = "/"}>
20        Powrót do Centrum Dowodzenia
21      </button>
22    </div>
23  );
24}

Pełny przykład konfiguracji dla złożonej aplikacji kosmicznej

Poniżej znajduje się kompleksowy przykład konfiguracji routingu dla zaawansowanej aplikacji:

1import {
2  BrowserRouter,
3  Routes,
4  Route,
5  Outlet,
6  Navigate,
7  useRouteError
8} from 'react-router-dom';
9
10// Layouts
11function MainLayout() {
12  return (
13    <div className="main-layout">
14      <MainNavigation />
15      <div className="content">
16        <Outlet />
17      </div>
18      <Footer />
19    </div>
20  );
21}
22
23function AuthLayout() {
24  return (
25    <div className="auth-layout">
26      <div className="auth-container">
27        <Outlet />
28      </div>
29    </div>
30  );
31}
32
33// Error Components
34function GlobalError() {
35  const error = useRouteError();
36  return (
37    <div className="error-screen">
38      <h1>Błąd systemu nawigacji</h1>
39      <p>{error.message || "Nieznany błąd"}</p>
40    </div>
41  );
42}
43
44function NotFound() {
45  return (
46    <div className="not-found">
47      <h1>Zgubiony w przestrzeni kosmicznej</h1>
48      <p>Statek nie odnalazł żądanej lokalizacji.</p>
49    </div>
50  );
51}
52
53// Protected Route Component
54function ProtectedRoute({ children }) {
55  const isAuthenticated = useAuth();
56
57  if (!isAuthenticated) {
58    return <Navigate to="/auth/login" replace />;
59  }
60
61  return children;
62}
63
64// Main App
65function App() {
66  return (
67    <BrowserRouter>
68      <Routes>
69        {/* Główna sekcja z layoutem */}
70        <Route path="/" element={<MainLayout />} errorElement={<GlobalError />}>
71          <Route index element={<Dashboard />} />
72
73          {/* Planety */}
74          <Route path="planets">
75            <Route index element={<PlanetsList />} />
76            <Route path=":planetId" element={<PlanetDetails />} />
77          </Route>
78
79          {/* Misje - wymagają uwierzytelnienia */}
80          <Route path="missions" element={
81            <ProtectedRoute>
82              <MissionsLayout />
83            </ProtectedRoute>
84          }>
85            <Route index element={<MissionsDashboard />} />
86            <Route path="new" element={<NewMission />} />
87            <Route path=":missionId" element={<MissionDetails />} />
88            <Route path=":missionId/edit" element={<EditMission />} />
89          </Route>
90
91          {/* Załoga */}
92          <Route path="crew">
93            <Route index element={<CrewList />} />
94            <Route path=":memberId" element={<CrewMemberProfile />} />
95          </Route>
96
97          {/* Ustawienia - wymagają uwierzytelnienia */}
98          <Route path="settings" element={
99            <ProtectedRoute>
100              <Settings />
101            </ProtectedRoute>
102          } />
103        </Route>
104
105        {/* Sekcja uwierzytelniania */}
106        <Route path="/auth" element={<AuthLayout />}>
107          <Route path="login" element={<Login />} />
108          <Route path="register" element={<Register />} />
109          <Route path="forgot-password" element={<ForgotPassword />} />
110          <Route path="reset-password/:token" element={<ResetPassword />} />
111        </Route>
112
113        {/* Dokumentacja publiczna */}
114        <Route path="/docs/*" element={<DocsLayout />} />
115
116        {/* Przekierowanie ze starej ścieżki na nową */}
117        <Route path="/old-missions" element={<Navigate to="/missions" replace />} />
118
119        {/* Obsługa 404 */}
120        <Route path="*" element={<NotFound />} />
121      </Routes>
122    </BrowserRouter>
123  );
124}

Konfiguracja z React Router w środowisku developerskim

Podczas rozwijania aplikacji kosmicznej, warto skonfigurować narzędzia developerskie do pracy z React Router:

1. React Router DevTools

1// Tylko w trybie deweloperskim
2if (process.env.NODE_ENV === 'development') {
3  const ReactRouterDevTools = React.lazy(() =>
4    import('react-router-devtools').then(module => ({
5      default: module.ReactRouterDevTools
6    }))
7  );
8
9  // Użycie w aplikacji
10  <React.Suspense fallback={null}>
11    <ReactRouterDevTools />
12  </React.Suspense>
13}

2. Konfiguracja webpack-dev-server dla BrowserRouter

Aby obsługiwać odświeżanie stron w środowisku developerskim z BrowserRouter, należy skonfigurować webpack-dev-server:

1// webpack.config.js
2module.exports = {
3  // inne ustawienia...
4  devServer: {
5    historyApiFallback: true, // Przekierowanie 404 do index.html
6    // inne ustawienia...
7  }
8};

Konfiguracja serwera produkcyjnego dla React Router

W środowisku produkcyjnym, serwer musi być skonfigurowany do obsługi wszystkich ścieżek React Router:

1. Dla serwera Node.js/Express:

1const express = require('express');
2const path = require('path');
3const app = express();
4
5// Serwowanie statycznych plików
6app.use(express.static(path.join(__dirname, 'build')));
7
8// Wszystkie żądania, które nie są do statycznych plików, przekierowane do index.html
9app.get('*', (req, res) => {
10  res.sendFile(path.join(__dirname, 'build', 'index.html'));
11});
12
13const PORT = process.env.PORT || 3000;
14app.listen(PORT, () => {
15  console.log(`Serwer nasłuchuje na porcie ${PORT}`);
16});

2. Dla serwera Apache (.htaccess):

1<IfModule mod_rewrite.c>
2  RewriteEngine On
3  RewriteBase /
4  RewriteRule ^index\.html$ - [L]
5  RewriteCond %{REQUEST_FILENAME} !-f
6  RewriteCond %{REQUEST_FILENAME} !-d
7  RewriteRule . /index.html [L]
8</IfModule>

3. Dla serwera Nginx:

1server {
2  listen 80;
3  server_name kosmiczna-aplikacja.pl;
4
5  root /var/www/html;
6  index index.html;
7
8  location / {
9    try_files $uri $uri/ /index.html;
10  }
11}

Podsumowanie

Prawidłowa konfiguracja routingu jest kluczowa dla funkcjonowania aplikacji kosmicznej (React). Kluczowe punkty:

  1. Wybierz odpowiedni typ routera - zwykle BrowserRouter dla nowoczesnych aplikacji
  2. Twórz hierarchiczną strukturę tras - używaj zagnieżdżonych tras z Outlet dla lepszej organizacji
  3. Zaimplementuj obsługę błędów - używaj errorElement dla obsługi błędów
  4. Zabezpiecz prywatne trasy - stwórz komponent ProtectedRoute do ochrony dostępu
  5. Skonfiguruj serwer produkcyjny - upewnij się, że serwer przekierowuje wszystkie żądania do index.html

Z dobrze skonfigurowanym systemem nawigacji, użytkownicy twojej kosmicznej aplikacji będą mogli swobodnie podróżować między różnymi jej częściami, bez obaw o zagubienie się w przestrzeni kosmicznej (błędy 404).

Kod do tej lekcji: App.jsx
1import React from 'react';
2import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
3
4// Strona główna
5function Home() {
6  return (
7    <div className="page home">
8      <h1>Strona główna</h1>
9      <p>Witaj w kosmicznym centrum nawigacji!</p>
10      <p>Użyj menu powyżej, aby nawigować między stronami.</p>
11    </div>
12  );
13}
14
15// Strona "O nas"
16function About() {
17  return (
18    <div className="page about">
19      <h1>O nas</h1>
20      <p>Jesteśmy zespołem badaczy kosmosu.</p>
21      <p>Nasza misja: eksploracja galaktyki przez React Router!</p>
22    </div>
23  );
24}
25
26// Strona kontaktowa
27function Contact() {
28  return (
29    <div className="page contact">
30      <h1>Kontakt</h1>
31      <p>Email: mission-control@space.com</p>
32      <p>Telefon: +48 123 456 789</p>
33    </div>
34  );
35}
36
37// Nawigacja
38function Navigation() {
39  return (
40    <nav className="navigation">
41      <Link to="/" className="nav-link">Home</Link>
42      <Link to="/about" className="nav-link">O nas</Link>
43      <Link to="/contact" className="nav-link">Kontakt</Link>
44    </nav>
45  );
46}
47
48function App() {
49  return (
50    <BrowserRouter>
51      <div className="app">
52        <header>
53          <h2>Space Navigator</h2>
54          <Navigation />
55        </header>
56
57        <main>
58          <Routes>
59            <Route path="/" element={<Home />} />
60            <Route path="/about" element={<About />} />
61            <Route path="/contact" element={<Contact />} />
62          </Routes>
63        </main>
64      </div>
65    </BrowserRouter>
66  );
67}
68
69export default App;

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Jaka jest główna różnica między BrowserRouter a HashRouter?

Zadania praktyczne w grze

  • Klikanie w kolejności

    Ułóż komendę instalacji React Router:

  • Edytor kodu

    Stwórz: <BrowserRouter><Routes><Route path='/' element={<Dashboard />} /><Route path='/planets' element={<Planets />} /><Route path='/missions' element={<Missions />} /></Routes></BrowserRouter>. Dodaj <Link to='/planets'>Planety</Link>!

  • Klikanie w kolejności

    Uporządkuj podstawową strukturę aplikacji React Router:

  • Edytor kodu

    Stwórz nawigację: <nav><NavLink to='/' className={({isActive}) => isActive ? 'active' : ''}>Home</NavLink><NavLink to='/asteroids'>Asteroidy</NavLink></nav>. Dodaj CSS: .active { color: gold; border-bottom: 2px solid gold }

Przydatne artykuły