Індексні маршрути
На більшості інтернет-сайтів певна частина інтерфейсу лишається незмінно. Наприклад шапка, підвал, бокове меню тощо. А при навігації по сайту змінюється лише вміст головного контейнера.
Немає сенсу на кожній сторінці дублювати цю розмітку. Можна зробити спільну обгортку із компонентів, які не змінюються, а при навігації по сайту змінювати тільки головний вміст. Для цього використовують прийом «shared layout».
// Import libraries and components
export const App = () => {
return (
<Container>
<Header>
<Logo>
<span role="img" aria-label="logo icon">
🙈
</span>
Monkey Studio
</Logo>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/reviews">Reviews</Link>
<Link to="/works">Works</Link>
</nav>
</Header>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />}>
<Route path="team" element={<Team />} />
<Route path="mission" element={<Mission />} />
<Route path="founders" element={<Founders />} />
<Route path="partners" element={<Partners />} />
</Route>
<Route path="/reviews" element={<Reviews />} />
<Route path="/works" element={<Works />} />
<Route path="/works/:workId" element={<WorkDetails />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Container>
);
};
У прикладі вище, головне меню навігації зверстано у файлі App.jsx. При нарощенні проєкту, стилізаційні елементи сильно перенавантажуватимуть App.jsx. Прийнято у файлі App.jsx прописувати тільки правила маршрутизації, без зайвого контенту, а всі стилізаційні компоненти та верстання незмінного блоку виносити в окремі блоки.
Тож, винесемо розмітку і стилі незмінної обгортки в окремий компонент <SharedLayout>. Там де ми плануємо рендерити змінну частину сайту в залежності маршруту на якому перебуваємо - вказуємо компонент <Outlet>.
// Import libraries and components
import { Outlet } from "react-router-dom";
export const SharedLayout = () => {
return (
<Container>
<Header>
<Logo>
<span role="img" aria-label="logo icon">
🙈
</span>
Monkey Studio
</Logo>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/reviews">Reviews</Link>
<Link to="/works">Works</Link>
</nav>
</Header>
<Outlet />
</Container>
);
};
Після цього потрібно використати компонент <SharedLayout> в компоненті <App>. Пропишемо так, щоб він рендерився на всіх маршрутах вкажемо його маршрут path="/". Вкладемо у нього всі інші маршрути АЛЕ маршрути пропишемо відносно батьківського Shared Layout - тобто prop path без символу /.
// Import libraries and components
import { SharedLayout } from "path/to/components/SharedLayout";
export const App = () => {
return (
<Routes>
<Route path="/" element={<SharedLayout />}>
<Route path="about" element={<About />}>
<Route path="team" element={<Team />} />
<Route path="mission" element={<Mission />} />
<Route path="founders" element={<Founders />} />
<Route path="partners" element={<Partners />} />
</Route>
<Route path="reviews" element={<Reviews />} />
<Route path="works" element={<Works />} />
<Route path="works/:workId" element={<WorkDetails />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
);
};
Тут видно, що за базовим маршрутом path="/" тепер рендериться тільки компонент <SharedLayout> без <Home>. Хоча за іншими вкладеними маршрутами рендериться як <SharedLayout>, так і той компонент у якого збігається path зі значенням в адресному рядку. Тобто за маршрутом path="about" зрендериться компонент <SharedLayout> і <About>.
Щоб виправити цей ньюанс і за базовим маршрутом рендерити <Home> компонент за тим же маршрутом необхідно створити індексний маршрут.
// Import libraries and components
import { SharedLayout } from "path/to/components/SharedLayout";
export const App = () => {
return (
<Routes>
<Route path="/" element={<SharedLayout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />}>
<Route path="team" element={<Team />} />
<Route path="mission" element={<Mission />} />
<Route path="founders" element={<Founders />} />
<Route path="partners" element={<Partners />} />
</Route>
<Route path="reviews" element={<Reviews />} />
<Route path="works" element={<Works />} />
<Route path="works/:workId" element={<WorkDetails />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
);
};
Індексним може бути лише вкладений маршрут. В його <Route> не вказують prop path, тому що він збігається зі значенням path батька. Замість цього передають спеціальний prop index, який вказує бібліотеці React Router, що цей маршрут треба відрендерити на ту ж адресу, що і його батько.
Індексних маршрутів може бути довільна кількість залежно від поставленого завдання. Наприклад, у панелі адміністратора можна рендерити інший інтерфейс і відповідно підключити інший <SharedLayout>.
// Import libraries and components
import { SharedLayout } from "path/to/components/SharedLayout";
export const App = () => {
return (
<Routes>
<Route path="/" element={<SharedLayout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />}>
<Route path="team" element={<Team />} />
<Route path="mission" element={<Mission />} />
<Route path="founders" element={<Founders />} />
<Route path="partners" element={<Partners />} />
</Route>
<Route path="reviews" element={<Reviews />} />
<Route path="works" element={<Works />} />
<Route path="works/:workId" element={<WorkDetails />} />
<Route path="*" element={<NotFound />} />
</Route>
<Route path="/admin" element={<AdminLayout />}>
<Route index element={<Dashboard />} />
<Route path="sales" element={<Sales />} />
<Route path="customers" element={<Customers />} />
</Route>
</Routes>
);
};
Нижче наведено живий приклад на маршрутизацію із SharedLayout. Проглянути код можна тут.
Last updated