Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Zagnieżdżone ścieżki w intergalaktycznej nawigacji

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.

Czym są zagnieżdżone ścieżki?

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.

Implementacja zagnieżdżonych ścieżek w React Router

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.

Podstawowa struktura zagnieżdżonych ś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:

  1. Layout
    to główny komponent, który będzie zawierał wszystkie zagnieżdżone widoki
  2. GalaxyLayout
    to komponent, który będzie służył jako "kontener" dla szczegółów galaktyki
  3. Atrybut
    index
    określa, co ma być renderowane, gdy URL pasuje dokładnie do ścieżki rodzica

Komponent Outlet

Kluczowym 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}

Względne i indeksowe ścieżki

React Router v6 wprowadził kilka użytecznych funkcji dla lepszej obsługi zagnieżdżonych ścieżek:

1. Ścieżki indeksowe (index routes)

Ś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>

2. Względne linki

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}

Przekazywanie kontekstu między poziomami zagnieżdżenia

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ń:

1. Hooki useOutletContext i useOutlet

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}

2. Wspólny stan z React Context

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}

Zaawansowane wzorce zagnieżdżania ścieżek

Zagnieżdżone ścieżki mogą być wykorzystywane do implementacji różnych wzorców nawigacyjnych:

1. Layout wielopoziomowy

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ę strony
  • AuthLayout
    obsługuje uwierzytelnianie dla chronionych ścieżek
  • SettingsLayout
    tworzy dodatkowy poziom nawigacji dla ustawień

2. Dynamiczne segmenty i zagnieżdżenie

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.

3. Wiele slotów za pomocą Named Outlets

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}

Optymalizacja zagnieżdżonych ścieżek

Przy pracy z zagnieżdżonymi ścieżkami, warto pamiętać o kilku technikach optymalizacji:

1. Lazy Loading komponentów

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}

2. Optymalizacja renderowania

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});

Przykład kompletnej aplikacji z zagnieżdżonymi ścieżkami

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;

Najlepsze praktyki dla zagnieżdżonych ścieżek

  1. Utrzymuj Logiczną Hierarchię - Struktura zagnieżdżonych ścieżek powinna odzwierciedlać logiczną organizację aplikacji
  2. Używaj Komponentów Layout - Twórz dedykowane komponenty layout dla każdego poziomu zagnieżdżenia
  3. Optymalizuj Przekazywanie Danych - Używaj useOutletContext lub React Context dla efektywnego przekazywania danych
  4. Zapewnij Intuicyjną Nawigację - Zawsze dodawaj linki pozwalające na łatwą nawigację w górę i w dół hierarchii
  5. Dziel Kod - Używaj lazy loading dla optymalizacji wydajności, szczególnie dla głęboko zagnieżdżonych komponentów
  6. Utrzymuj Czystość URL - Projektuj URL-e tak, aby były czytelne i logiczne dla użytkownika
  7. Unikaj Zbyt Głębokiego Zagnieżdżenia - Staraj się nie przekraczać 3-4 poziomów zagnieżdżenia dla zachowania przejrzystości

Podsumowanie

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:

  1. Logiczna Organizacja - Struktura routingu odzwierciedla logiczną hierarchię aplikacji
  2. Ponowne Użycie Layoutów - Wspólne elementy UI mogą być współdzielone między podścieżkami
  3. Lepsze UX - Użytkownicy mogą łatwo orientować się w strukturze aplikacji
  4. Łatwiejsze Zarządzanie Stanem - Stan może być przechowywany na odpowiednim poziomie zagnieżdżenia

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.

Przejdź do CodeWorlds