Kurs JavaScript i React · Moduł 6: React Router
Konfiguracja routingu w aplikacjach kosmicznych
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:
SpaceLayoutjest głównym layoutem dla ścieżek zaczynających się od "/"PlanetLayoutjest layoutem dla wszystkich ścieżek z sekcji "planets"- Element
indexokreś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 © 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:
- Wybierz odpowiedni typ routera - zwykle
BrowserRouterdla nowoczesnych aplikacji - Twórz hierarchiczną strukturę tras - używaj zagnieżdżonych tras z
Outletdla lepszej organizacji - Zaimplementuj obsługę błędów - używaj
errorElementdla obsługi błędów - Zabezpiecz prywatne trasy - stwórz komponent
ProtectedRoutedo ochrony dostępu - 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. 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 }