W naszej kosmicznej podróży przez świat React Router, nadszedł czas na zgłębienie koncepcji zagnieżdżonych ścieżek. Podobnie jak w rzeczywistej przestrzeni kosmicznej, gdzie mamy galaktyki zawierające układy słoneczne, które z kolei zawierają planety z ich księżycami - w aplikacjach React możemy tworzyć hierarchiczne struktury nawigacyjne.
Zagnieżdżone ścieżki to technika organizowania routingu w aplikacji, gdzie jedne ścieżki (komponenty) zawierają w sobie inne ścieżki (komponenty). Pozwala to na tworzenie bardziej intuicyjnych, hierarchicznych struktur nawigacyjnych, które odzwierciedlają logiczny układ aplikacji.
Zagnieżdżone ścieżki są szczególnie przydatne w większych aplikacjach, gdzie mamy wiele podsekcji z własnymi widokami.
W React Router v6 zagnieżdżone ścieżki implementuje się poprzez umieszczanie komponentów
Route wewnątrz innych komponentów Route oraz używanie komponentu Outlet do określenia, gdzie mają być renderowane komponenty podścieżek.1import { BrowserRouter, Routes, Route, Outlet } from 'react-router-dom';
2
3function App() {
4 return (
5 <BrowserRouter>
6 <Routes>
7 <Route path="/" element={<Layout />}>
8 <Route index element={<Home />} />
9 <Route path="galaxies" element={<GalaxiesList />} />
10 <Route path="galaxies/:galaxyId" element={<GalaxyLayout />}>
11 <Route index element={<GalaxyOverview />} />
12 <Route path="systems" element={<SolarSystemsList />} />
13 <Route path="systems/:systemId" element={<SolarSystemDetail />} />
14 </Route>
15 </Route>
16 </Routes>
17 </BrowserRouter>
18 );
19}W powyższym przykładzie:
Layout to główny komponent, który będzie zawierał wszystkie zagnieżdżone widokiGalaxyLayout to komponent, który będzie służył jako "kontener" dla szczegółów galaktykiindex określa, co ma być renderowane, gdy URL pasuje dokładnie do ścieżki rodzicaKluczowym elementem zagnieżdżonych ścieżek jest komponent
Outlet, który działa jak "placeholder" dla komponentów potomnych. Określa on, gdzie w komponencie nadrzędnym mają być renderowane komponenty podścieżek:1import { Outlet, Link } from 'react-router-dom';
2
3function Layout() {
4 return (
5 <div className="app-layout">
6 <header>
7 <h1>Kosmiczny Atlas</h1>
8 <nav>
9 <Link to="/">Strona główna</Link>
10 <Link to="/galaxies">Galaktyki</Link>
11 </nav>
12 </header>
13
14 <main>
15 {/* Tutaj będą renderowane komponenty podścieżek */}
16 <Outlet />
17 </main>
18
19 <footer>© 2150 Międzygalaktyczne Centrum Nawigacji</footer>
20 </div>
21 );
22}
23
24function GalaxyLayout() {
25 const { galaxyId } = useParams();
26
27 return (
28 <div className="galaxy-layout">
29 <h2>Galaktyka: {galaxyId}</h2>
30 <nav className="galaxy-navigation">
31 <Link to={`/galaxies/${galaxyId}`}>Przegląd</Link>
32 <Link to={`/galaxies/${galaxyId}/systems`}>Układy słoneczne</Link>
33 </nav>
34
35 <div className="galaxy-content">
36 {/* Tutaj będą renderowane komponenty podścieżek galaktyki */}
37 <Outlet />
38 </div>
39 </div>
40 );
41}React Router v6 wprowadził kilka użytecznych funkcji dla lepszej obsługi zagnieżdżonych ścieżek:
Ścieżka indeksowa określa, co powinno być renderowane, gdy URL pasuje dokładnie do ścieżki nadrzędnej:
1<Route path="galaxies" element={<GalaxiesLayout />}>
2 {/* To będzie renderowane dla /galaxies */}
3 <Route index element={<GalaxiesList />} />
4
5 {/* To będzie renderowane dla /galaxies/:galaxyId */}
6 <Route path=":galaxyId" element={<GalaxyDetails />} />
7</Route>W komponentach zagnieżdżonych możemy używać względnych linków, które są rozwiązywane względem bieżącej ścieżki:
1import { Link, useParams } from 'react-router-dom';
2
3function GalaxySystemsList() {
4 const { galaxyId } = useParams();
5
6 return (
7 <div>
8 <h3>Układy słoneczne w galaktyce {galaxyId}</h3>
9 <ul>
10 {systems.map(system => (
11 <li key={system.id}>
12 {/* Względny link - zostanie rozwiązany do /galaxies/:galaxyId/systems/:systemId */}
13 <Link to={system.id}>{system.name}</Link>
14 </li>
15 ))}
16 </ul>
17
18 {/* Względny link "w górę" - cofnij się o jeden poziom */}
19 <Link to="..">Powrót do przeglądu galaktyki</Link>
20 </div>
21 );
22}Jednym z wyzwań przy pracy z zagnieżdżonymi ścieżkami jest przekazywanie danych między różnymi poziomami zagnieżdżenia. React Router v6 oferuje kilka rozwiązań:
Hook
useOutletContext pozwala na przekazywanie danych z komponentu nadrzędnego do podrzędnego:1import { Outlet, useOutletContext, useParams } from 'react-router-dom';
2
3function GalaxyLayout() {
4 const { galaxyId } = useParams();
5 const [galaxy, setGalaxy] = useState(null);
6
7 useEffect(() => {
8 // Pobierz dane galaktyki
9 fetchGalaxy(galaxyId).then(data => setGalaxy(data));
10 }, [galaxyId]);
11
12 // Przekaż dane galaktyki do komponentów potomnych
13 return (
14 <div className="galaxy-layout">
15 <h2>{galaxy?.name || 'Ładowanie...'}</h2>
16 <Outlet context={{ galaxy, updateGalaxy: setGalaxy }} />
17 </div>
18 );
19}
20
21function GalaxyOverview() {
22 // Odbierz kontekst z komponentu nadrzędnego
23 const { galaxy, updateGalaxy } = useOutletContext();
24
25 if (!galaxy) return <div>Ładowanie danych galaktyki...</div>;
26
27 return (
28 <div className="galaxy-overview">
29 <h3>Przegląd galaktyki {galaxy.name}</h3>
30 <p>Typ: {galaxy.type}</p>
31 <p>Liczba układów: {galaxy.systemsCount}</p>
32 {/* Reszta komponentu */}
33 </div>
34 );
35}Dla bardziej złożonych scenariuszy można wykorzystać React Context do udostępniania stanu między komponentami:
1import { createContext, useContext, useState } from 'react';
2import { Outlet } from 'react-router-dom';
3
4// Stwórz kontekst
5const GalaxyContext = createContext();
6
7// Hook do łatwego dostępu do kontekstu
8function useGalaxy() {
9 return useContext(GalaxyContext);
10}
11
12function GalaxyProvider({ children }) {
13 const [selectedGalaxy, setSelectedGalaxy] = useState(null);
14 const [favoritesSystems, setFavoriteSystems] = useState([]);
15
16 const addFavoriteSystem = (system) => {
17 setFavoriteSystems(prev => [...prev, system]);
18 };
19
20 // Wartość kontekstu
21 const value = {
22 selectedGalaxy,
23 setSelectedGalaxy,
24 favoritesSystems,
25 addFavoriteSystem
26 };
27
28 return (
29 <GalaxyContext.Provider value={value}>
30 {children}
31 </GalaxyContext.Provider>
32 );
33}
34
35function GalaxiesRoot() {
36 return (
37 <GalaxyProvider>
38 <div className="galaxies-section">
39 <h2>Eksploracja Galaktyk</h2>
40 <Outlet />
41 </div>
42 </GalaxyProvider>
43 );
44}
45
46// Użycie w komponencie potomnym
47function SolarSystemDetail() {
48 const { favoritesSystems, addFavoriteSystem } = useGalaxy();
49 const { systemId } = useParams();
50
51 // Reszta komponentu
52}Zagnieżdżone ścieżki mogą być wykorzystywane do implementacji różnych wzorców nawigacyjnych:
1<Routes>
2 <Route path="/" element={<MainLayout />}>
3 <Route element={<AuthLayout />}>
4 <Route path="dashboard" element={<Dashboard />} />
5 <Route path="profile" element={<Profile />} />
6 <Route path="settings" element={<SettingsLayout />}>
7 <Route path="account" element={<AccountSettings />} />
8 <Route path="notifications" element={<NotificationSettings />} />
9 </Route>
10 </Route>
11 <Route path="about" element={<About />} />
12 <Route path="contact" element={<Contact />} />
13 </Route>
14</Routes>W tym przykładzie:
MainLayout zawiera ogólną strukturę stronyAuthLayout obsługuje uwierzytelnianie dla chronionych ścieżekSettingsLayout tworzy dodatkowy poziom nawigacji dla ustawień1<Routes>
2 <Route path="galaxies" element={<GalaxiesList />} />
3 <Route path="galaxies/:galaxyId" element={<GalaxyLayout />}>
4 <Route index element={<GalaxyOverview />} />
5 <Route path="systems" element={<SystemsList />} />
6 <Route path="systems/:systemId" element={<SystemLayout />}>
7 <Route index element={<SystemOverview />} />
8 <Route path="planets" element={<PlanetsList />} />
9 <Route path="planets/:planetId" element={<PlanetDetails />} />
10 </Route>
11 </Route>
12</Routes>Ta struktura tworzy głębokie zagnieżdżenie ścieżek, które odzwierciedla hierarchię galaktyka > układ słoneczny > planeta.
React Router v6 wprowadził koncepcję nazwanych outletsów, co pozwala na renderowanie wielu komponentów potomnych w różnych miejscach:
1function DashboardLayout() {
2 return (
3 <div className="dashboard">
4 <aside className="sidebar">
5 {/* Named outlet dla nawigacji bocznej */}
6 <Outlet name="sidebar" />
7 </aside>
8 <div className="main-content">
9 {/* Domyślny outlet dla głównej zawartości */}
10 <Outlet />
11 </div>
12 <aside className="auxiliary-panel">
13 {/* Named outlet dla panelu pomocniczego */}
14 <Outlet name="auxiliary" />
15 </aside>
16 </div>
17 );
18}Przy pracy z zagnieżdżonymi ścieżkami, warto pamiętać o kilku technikach optymalizacji:
Dla poprawy wydajności, komponenty mogą być ładowane dynamicznie tylko wtedy, gdy są potrzebne:
1import { Suspense, lazy } from 'react';
2import { Routes, Route } from 'react-router-dom';
3
4// Lazy loading komponentów
5const GalaxyLayout = lazy(() => import('./GalaxyLayout'));
6const GalaxiesList = lazy(() => import('./GalaxiesList'));
7const SolarSystemDetails = lazy(() => import('./SolarSystemDetails'));
8
9function App() {
10 return (
11 <Routes>
12 <Route path="/" element={<MainLayout />}>
13 {/* Komponent ładowany leniwie */}
14 <Route
15 path="galaxies"
16 element={
17 <Suspense fallback={<div>Ładowanie galaktyk...</div>}>
18 <GalaxiesList />
19 </Suspense>
20 }
21 />
22 <Route
23 path="galaxies/:galaxyId/*"
24 element={
25 <Suspense fallback={<div>Ładowanie danych galaktyki...</div>}>
26 <GalaxyLayout />
27 </Suspense>
28 }
29 />
30 </Route>
31 </Routes>
32 );
33}Optymalizacja renderowania może być szczególnie ważna przy głębokim zagnieżdżeniu, gdy zmiana parametru URL na głębokim poziomie może spowodować zbędne rerenderowanie komponentów wyższego poziomu:
1import { memo } from 'react';
2import { Outlet, useParams } from 'react-router-dom';
3
4// Komponent zoptymalizowany - nie będzie się rerenderował, jeśli jego props się nie zmienią
5const MemoizedGalaxyLayout = memo(function GalaxyLayout() {
6 const { galaxyId } = useParams();
7
8 console.log(`Renderowanie GalaxyLayout dla galaktyki ${galaxyId}`);
9
10 return (
11 <div className="galaxy-layout">
12 <h2>Galaktyka: {galaxyId}</h2>
13 <Outlet />
14 </div>
15 );
16});Poniżej znajduje się przykład kompletnej aplikacji kosmicznej z zagnieżdżonymi ścieżkami:
1import React from 'react';
2import {
3 BrowserRouter,
4 Routes,
5 Route,
6 Outlet,
7 Link,
8 NavLink,
9 useParams,
10 useOutletContext,
11 useNavigate
12} from 'react-router-dom';
13
14// Główny layout aplikacji
15function AppLayout() {
16 return (
17 <div className="cosmos-explorer">
18 <header className="main-header">
19 <h1>Kosmiczny Atlas Podróżnika</h1>
20 <MainNavigation />
21 </header>
22
23 <main className="main-content">
24 <Outlet />
25 </main>
26
27 <footer className="main-footer">
28 <p>© 2150 Międzygalaktyczna Agencja Podróży</p>
29 </footer>
30 </div>
31 );
32}
33
34// Główna nawigacja
35function MainNavigation() {
36 return (
37 <nav className="main-nav">
38 <ul>
39 <li><NavLink to="/" end>Centrum Dowodzenia</NavLink></li>
40 <li><NavLink to="/galaxies">Galaktyki</NavLink></li>
41 <li><NavLink to="/missions">Misje</NavLink></li>
42 <li><NavLink to="/crew">Załoga</NavLink></li>
43 </ul>
44 </nav>
45 );
46}
47
48// Strona główna
49function Dashboard() {
50 return (
51 <div className="dashboard">
52 <h2>Centrum Dowodzenia Statku Kosmicznego</h2>
53 <p>Witaj, Kapitanie! Jaką lokalizację chcesz dziś eksplorować?</p>
54
55 <div className="dashboard-tiles">
56 <div className="tile">
57 <h3>Galaktyki</h3>
58 <p>Eksploruj różne galaktyki we wszechświecie</p>
59 <Link to="/galaxies" className="tile-link">Przejdź do galaktyk</Link>
60 </div>
61
62 <div className="tile">
63 <h3>Misje</h3>
64 <p>Przeglądaj aktualne i przyszłe misje kosmiczne</p>
65 <Link to="/missions" className="tile-link">Przejdź do misji</Link>
66 </div>
67
68 <div className="tile">
69 <h3>Załoga</h3>
70 <p>Zarządzaj członkami załogi statku kosmicznego</p>
71 <Link to="/crew" className="tile-link">Przejdź do załogi</Link>
72 </div>
73 </div>
74 </div>
75 );
76}
77
78// ===== SEKCJA GALAKTYK =====
79
80// Layout dla galaktyk
81function GalaxiesLayout() {
82 return (
83 <div className="galaxies-section">
84 <h2>Eksploracja Galaktyk</h2>
85 <Outlet />
86 </div>
87 );
88}
89
90// Lista galaktyk
91function GalaxiesList() {
92 const galaxies = [
93 { id: 'milky-way', name: 'Droga Mleczna', type: 'Spiralna' },
94 { id: 'andromeda', name: 'Andromeda', type: 'Spiralna' },
95 { id: 'triangulum', name: 'Galaktyka Trójkąta', type: 'Spiralna' },
96 { id: 'large-magellanic', name: 'Wielki Obłok Magellana', type: 'Nieregularna' }
97 ];
98
99 return (
100 <div className="galaxies-list">
101 <h3>Dostępne Galaktyki</h3>
102 <div className="galaxy-cards">
103 {galaxies.map(galaxy => (
104 <div key={galaxy.id} className="galaxy-card">
105 <h4>{galaxy.name}</h4>
106 <p>Typ: {galaxy.type}</p>
107 <Link to={galaxy.id} className="galaxy-link">
108 Eksploruj galaktykę
109 </Link>
110 </div>
111 ))}
112 </div>
113 </div>
114 );
115}
116
117// Layout szczegółów galaktyki
118function GalaxyLayout() {
119 const { galaxyId } = useParams();
120 const navigate = useNavigate();
121
122 // Symulacja danych galaktyki (w prawdziwej aplikacji pobierane z API)
123 const galaxyData = {
124 'milky-way': {
125 name: 'Droga Mleczna',
126 type: 'Spiralna',
127 diameter: '100,000 lat świetlnych',
128 stars: '200-400 miliardów',
129 systems: [
130 { id: 'solar', name: 'Układ Słoneczny' },
131 { id: 'alpha-centauri', name: 'Alfa Centauri' },
132 { id: 'sirius', name: 'Syriusz' }
133 ]
134 },
135 'andromeda': {
136 name: 'Andromeda',
137 type: 'Spiralna',
138 diameter: '220,000 lat świetlnych',
139 stars: '1 bilion',
140 systems: [
141 { id: 'system-1', name: 'Układ M31-A' },
142 { id: 'system-2', name: 'Układ M31-B' }
143 ]
144 },
145 'triangulum': {
146 name: 'Galaktyka Trójkąta',
147 type: 'Spiralna',
148 diameter: '60,000 lat świetlnych',
149 stars: '40 miliardów',
150 systems: [
151 { id: 'system-1', name: 'Układ T1' }
152 ]
153 },
154 'large-magellanic': {
155 name: 'Wielki Obłok Magellana',
156 type: 'Nieregularna',
157 diameter: '14,000 lat świetlnych',
158 stars: '30 miliardów',
159 systems: []
160 }
161 };
162
163 const galaxy = galaxyData[galaxyId];
164
165 if (!galaxy) {
166 return (
167 <div className="galaxy-not-found">
168 <h3>Galaktyka nie znaleziona</h3>
169 <p>Nie znaleziono galaktyki o identyfikatorze: {galaxyId}</p>
170 <button onClick={() => navigate('/galaxies')}>
171 Powrót do listy galaktyk
172 </button>
173 </div>
174 );
175 }
176
177 return (
178 <div className="galaxy-details">
179 <h3>Galaktyka: {galaxy.name}</h3>
180
181 <nav className="galaxy-nav">
182 <NavLink to="" end>Przegląd</NavLink>
183 <NavLink to="systems">Układy Słoneczne</NavLink>
184 <NavLink to="map">Mapa Galaktyki</NavLink>
185 </nav>
186
187 <div className="galaxy-content">
188 <Outlet context={{ galaxy }} />
189 </div>
190
191 <div className="back-navigation">
192 <Link to="/galaxies">← Powrót do listy galaktyk</Link>
193 </div>
194 </div>
195 );
196}
197
198// Przegląd galaktyki (domyślny widok)
199function GalaxyOverview() {
200 const { galaxy } = useOutletContext();
201
202 return (
203 <div className="galaxy-overview">
204 <h4>Przegląd: {galaxy.name}</h4>
205 <table className="galaxy-stats">
206 <tbody>
207 <tr>
208 <th>Typ:</th>
209 <td>{galaxy.type}</td>
210 </tr>
211 <tr>
212 <th>Średnica:</th>
213 <td>{galaxy.diameter}</td>
214 </tr>
215 <tr>
216 <th>Liczba gwiazd:</th>
217 <td>{galaxy.stars}</td>
218 </tr>
219 <tr>
220 <th>Odkryte układy:</th>
221 <td>{galaxy.systems.length}</td>
222 </tr>
223 </tbody>
224 </table>
225
226 <h5>Ciekawostki</h5>
227 <p>Tutaj znajdą się unikalne ciekawostki o galaktyce {galaxy.name}...</p>
228 </div>
229 );
230}
231
232// Lista układów słonecznych w galaktyce
233function GalaxySystems() {
234 const { galaxy } = useOutletContext();
235 const { galaxyId } = useParams();
236
237 return (
238 <div className="galaxy-systems">
239 <h4>Układy Słoneczne w {galaxy.name}</h4>
240
241 {galaxy.systems.length > 0 ? (
242 <ul className="systems-list">
243 {galaxy.systems.map(system => (
244 <li key={system.id} className="system-item">
245 <div className="system-info">
246 <h5>{system.name}</h5>
247 </div>
248 <Link to={`/galaxies/${galaxyId}/systems/${system.id}`}>
249 Eksploruj układ
250 </Link>
251 </li>
252 ))}
253 </ul>
254 ) : (
255 <p>Nie odkryto jeszcze żadnych układów w tej galaktyce.</p>
256 )}
257 </div>
258 );
259}
260
261// Mapa galaktyki
262function GalaxyMap() {
263 const { galaxy } = useOutletContext();
264
265 return (
266 <div className="galaxy-map">
267 <h4>Mapa Galaktyki: {galaxy.name}</h4>
268 <div className="map-placeholder">
269 <p>Tutaj będzie interaktywna mapa galaktyki {galaxy.name}.</p>
270 <p>Obecnie w fazie rozwoju...</p>
271 </div>
272 </div>
273 );
274}
275
276// ===== SEKCJA UKŁADÓW SŁONECZNYCH =====
277
278// Szczegóły układu słonecznego
279function SolarSystemDetails() {
280 const { galaxyId, systemId } = useParams();
281 const navigate = useNavigate();
282
283 // W prawdziwej aplikacji, dane byłyby pobierane na podstawie galaxyId i systemId
284 const systemData = {
285 'solar': {
286 name: 'Układ Słoneczny',
287 star: 'Słońce',
288 planets: 8,
289 age: '4.6 miliarda lat'
290 },
291 'alpha-centauri': {
292 name: 'Alfa Centauri',
293 star: 'Alfa Centauri A i B, Proxima Centauri',
294 planets: 3,
295 age: '4.85 miliarda lat'
296 },
297 'sirius': {
298 name: 'Syriusz',
299 star: 'Syriusz A i B',
300 planets: 0,
301 age: '230 milionów lat'
302 },
303 'system-1': {
304 name: 'Układ M31-A',
305 star: 'Gwiazda M31-A',
306 planets: 5,
307 age: '2.3 miliarda lat'
308 },
309 'system-2': {
310 name: 'Układ M31-B',
311 star: 'Gwiazda M31-B',
312 planets: 7,
313 age: '3.1 miliarda lat'
314 }
315 };
316
317 const system = systemData[systemId];
318
319 if (!system) {
320 return (
321 <div className="system-not-found">
322 <h4>Układ nie znaleziony</h4>
323 <p>Nie znaleziono układu słonecznego o identyfikatorze: {systemId}</p>
324 <button onClick={() => navigate(`/galaxies/${galaxyId}/systems`)}>
325 Powrót do listy układów
326 </button>
327 </div>
328 );
329 }
330
331 return (
332 <div className="system-details">
333 <h4>Układ Słoneczny: {system.name}</h4>
334
335 <table className="system-stats">
336 <tbody>
337 <tr>
338 <th>Gwiazda(y):</th>
339 <td>{system.star}</td>
340 </tr>
341 <tr>
342 <th>Liczba planet:</th>
343 <td>{system.planets}</td>
344 </tr>
345 <tr>
346 <th>Wiek:</th>
347 <td>{system.age}</td>
348 </tr>
349 </tbody>
350 </table>
351
352 <div className="back-navigation">
353 <Link to={`/galaxies/${galaxyId}/systems`}>
354 ← Powrót do układów słonecznych
355 </Link>
356 </div>
357 </div>
358 );
359}
360
361// ===== SEKCJA MISJI =====
362
363// Layout misji
364function MissionsLayout() {
365 return (
366 <div className="missions-section">
367 <h2>Misje Kosmiczne</h2>
368 <Outlet />
369 </div>
370 );
371}
372
373// Lista misji
374function MissionsList() {
375 const missions = [
376 { id: 'apollo', name: 'Apollo', status: 'Zakończona' },
377 { id: 'artemis', name: 'Artemis', status: 'W trakcie' },
378 { id: 'mars-2030', name: 'Mars 2030', status: 'Planowana' }
379 ];
380
381 return (
382 <div className="missions-list">
383 <h3>Misje kosmiczne</h3>
384 <ul>
385 {missions.map(mission => (
386 <li key={mission.id}>
387 <Link to={mission.id}>{mission.name}</Link> - {mission.status}
388 </li>
389 ))}
390 </ul>
391 </div>
392 );
393}
394
395// Szczegóły misji
396function MissionDetails() {
397 const { missionId } = useParams();
398
399 return (
400 <div className="mission-details">
401 <h3>Szczegóły misji: {missionId}</h3>
402 <p>Tutaj będą szczegóły misji {missionId}...</p>
403 <Link to="/missions">← Powrót do listy misji</Link>
404 </div>
405 );
406}
407
408// ===== SEKCJA ZAŁOGI =====
409
410// Layout załogi
411function CrewLayout() {
412 return (
413 <div className="crew-section">
414 <h2>Zarządzanie Załogą</h2>
415 <Outlet />
416 </div>
417 );
418}
419
420// Lista członków załogi
421function CrewList() {
422 const crewMembers = [
423 { id: 'crew1', name: 'Jan Kowalski', role: 'Kapitan' },
424 { id: 'crew2', name: 'Anna Nowak', role: 'Nawigator' },
425 { id: 'crew3', name: 'Piotr Wiśniewski', role: 'Inżynier' }
426 ];
427
428 return (
429 <div className="crew-list">
430 <h3>Członkowie załogi</h3>
431 <ul>
432 {crewMembers.map(member => (
433 <li key={member.id}>
434 <Link to={member.id}>{member.name}</Link> - {member.role}
435 </li>
436 ))}
437 </ul>
438 </div>
439 );
440}
441
442// Szczegóły członka załogi
443function CrewMemberDetails() {
444 const { crewId } = useParams();
445
446 return (
447 <div className="crew-member-details">
448 <h3>Profil członka załogi: {crewId}</h3>
449 <p>Tutaj będzie profil członka załogi {crewId}...</p>
450 <Link to="/crew">← Powrót do listy załogi</Link>
451 </div>
452 );
453}
454
455// ===== GŁÓWNY KOMPONENT APLIKACJI =====
456
457function App() {
458 return (
459 <BrowserRouter>
460 <Routes>
461 <Route path="/" element={<AppLayout />}>
462 {/* Strona główna */}
463 <Route index element={<Dashboard />} />
464
465 {/* Sekcja galaktyk */}
466 <Route path="galaxies" element={<GalaxiesLayout />}>
467 <Route index element={<GalaxiesList />} />
468 <Route path=":galaxyId" element={<GalaxyLayout />}>
469 <Route index element={<GalaxyOverview />} />
470 <Route path="systems" element={<GalaxySystems />} />
471 <Route path="map" element={<GalaxyMap />} />
472 </Route>
473 <Route path=":galaxyId/systems/:systemId" element={<SolarSystemDetails />} />
474 </Route>
475
476 {/* Sekcja misji */}
477 <Route path="missions" element={<MissionsLayout />}>
478 <Route index element={<MissionsList />} />
479 <Route path=":missionId" element={<MissionDetails />} />
480 </Route>
481
482 {/* Sekcja załogi */}
483 <Route path="crew" element={<CrewLayout />}>
484 <Route index element={<CrewList />} />
485 <Route path=":crewId" element={<CrewMemberDetails />} />
486 </Route>
487
488 {/* Obsługa 404 */}
489 <Route path="*" element={<NotFound />} />
490 </Route>
491 </Routes>
492 </BrowserRouter>
493 );
494}
495
496// Komponent dla nieistniejących ścieżek
497function NotFound() {
498 return (
499 <div className="not-found">
500 <h2>404 - Zagubiony w kosmosie</h2>
501 <p>Ta ścieżka nie prowadzi do żadnego znanego celu w kosmosie.</p>
502 <Link to="/">Powrót do Centrum Dowodzenia</Link>
503 </div>
504 );
505}
506
507export default App;Zagnieżdżone ścieżki w React Router to potężna technika organizacji nawigacji w aplikacjach React, pozwalająca na tworzenie intuicyjnych, hierarchicznych struktur interfejsu użytkownika. Kluczowe korzyści:
Podobnie jak w rzeczywistej eksploracji kosmosu, gdzie podróżujemy od galaktyki, przez układy słoneczne, do planet i księżyców - zagnieżdżone ścieżki pozwalają tworzyć intuicyjne systemy nawigacji, które prowadzą użytkownika przez różne poziomy naszej aplikacji kosmicznej.