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.
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:
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).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, /#/jupiterMemoryRouter 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}Komponent
Route oferuje więcej opcji niż tylko podstawowe mapowanie ścieżki do elementu: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.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)1<Route path="/files/*" element={<FileExplorer />} />Gwiazdka łapie wszystko po
/files/, np. /files/documents/reports/annual.pdf.W obszernych aplikacjach kosmicznych, routing może stać się skomplikowany. Oto jak można go zorganizować:
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"index określa, co ma być renderowane dla ścieżki nadrzędnej (np. "/planets")W React Router v6 używamy propsa
element zamiast component z wcześniejszych wersji:1// React Router v6 (aktualny)
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/>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 © 2150</p>
18 </footer>
19 </div>
20 );
21}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}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}Podczas rozwijania aplikacji kosmicznej, warto skonfigurować narzędzia developerskie do pracy z React Router:
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}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};W środowisku produkcyjnym, serwer musi być skonfigurowany do obsługi wszystkich ścieżek React Router:
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});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>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}Prawidłowa konfiguracja routingu jest kluczowa dla funkcjonowania aplikacji kosmicznej (React). Kluczowe punkty:
BrowserRouter dla nowoczesnych aplikacjiOutlet dla lepszej organizacjierrorElement dla obsługi błędówProtectedRoute do ochrony dostępuZ 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).