Пошук уроків, статей та іншого контенту
Організуєте вкладені маршрути для складних розділів застосунку та відобразите дочірні сторінки через Outlet.
Вкладені маршрути використовують, коли один розділ застосунку має власну структуру сторінок.
Наприклад, розділ «Панель керування» може містити:
головну сторінку панелі;
список замовлень;
профіль користувача;
налаштування.
У такому випадку URL можуть мати спільний префікс:
/dashboard
/dashboard/orders
/dashboard/profile
/dashboard/settingsБатьківський маршрут відповідає за спільний інтерфейс розділу, а дочірній — за конкретний вміст.
Для відображення дочірнього маршруту в React Router використовується компонент
OutletOutletOutlet — це місце, у якому React Router відрендерить активний дочірній маршрут.
import { Outlet } from "react-router-dom";
function DashboardLayout() {
return (
<section>
<h1>Панель керування</h1>
<Outlet />
</section>
);
}Якщо для DashboardLayout активним є маршрут /dashboard/orders, компонент OrdersPage буде відображено замість Outlet.
Логічно це можна уявити так:
DashboardLayout
├── заголовок панелі
└── Outlet
└── OrdersPageБез Outlet дочірній маршрут може бути правильно зіставлений з URL, але його інтерфейс не з’явиться на сторінці.
Вкладені маршрути оголошують, розміщуючи дочірні компоненти Route всередині батьківського Route.
<Routes>
<Route path="dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
<Route path="orders" element={<OrdersPage />} />
<Route path="profile" element={<ProfilePage />} />
</Route>
</Routes>У цьому прикладі:
dashboard — батьківський маршрут;
index — дочірній маршрут за замовчуванням;
orders відповідає URL /dashboard/orders;
profile відповідає URL /dashboard/profile.
Шляхи дочірніх маршрутів записують без початкового /. Вони автоматично додаються до шляху батьківського маршруту.
Індексний маршрут відображається, коли активний шлях батьківського маршруту, але жоден додатковий дочірній шлях не вказано.
<Route path="dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
<Route path="orders" element={<OrdersPage />} />
</Route>Тоді:
/dashboard відображає DashboardHome;
/dashboard/orders відображає OrdersPage.
Індексний маршрут не має власного атрибута path, оскільки він використовує шлях батьківського маршруту.
Для переходів між маршрутами можна використовувати Link або NavLink.
Усередині батьківського маршруту зручно використовувати відносні шляхи:
<NavLink to="orders">Замовлення</NavLink>
<NavLink to="profile">Профіль</NavLink>Якщо ці посилання знаходяться на маршруті /dashboard, вони ведуть на:
/dashboard/orders
/dashboard/profileВідносний шлях не починається з /.
Нижче наведено приклад застосунку з панеллю керування та трьома дочірніми сторінками.
import {
BrowserRouter,
NavLink,
Outlet,
Route,
Routes,
} from "react-router-dom";
function HomePage() {
return (
<main>
<h1>Головна сторінка</h1>
<p>Ласкаво просимо до застосунку.</p>
</main>
);
}
function DashboardLayout() {
return (
<main>
<h1>Панель керування</h1>
<nav>
<NavLink to=".">Огляд</NavLink>{" | "}
<NavLink to="orders">Замовлення</NavLink>{" | "}
<NavLink to="profile">Профіль</NavLink>
</nav>
<hr />
<Outlet />
</main>
);
}
function DashboardHome() {
return (
<section>
<h2>Огляд</h2>
<p>Основна інформація про ваш обліковий запис.</p>
</section>
);
}
function OrdersPage() {
return (
<section>
<h2>Замовлення</h2>
<p>Тут відображається список замовлень.</p>
</section>
);
}
function ProfilePage() {
return (
<section>
<h2>Профіль</h2>
<p>Тут можна переглянути дані профілю.</p>
</section>
);
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
<Route path="orders" element={<OrdersPage />} />
<Route path="profile" element={<ProfilePage />} />
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;У цьому прикладі:
DashboardLayout відображає заголовок і навігацію.
Outlet є контейнером для дочірніх сторінок.
DashboardHome відображається на /dashboard.
OrdersPage відображається на /dashboard/orders.
ProfilePage відображається на /dashboard/profile.
Маршрути можна вкладати більше ніж на один рівень. Наприклад, сторінка замовлення може мати окремі підсторінки:
/dashboard/orders
/dashboard/orders/active
/dashboard/orders/archiveКонфігурація може виглядати так:
<Routes>
<Route path="dashboard" element={<DashboardLayout />}>
<Route path="orders" element={<OrdersLayout />}>
<Route index element={<ActiveOrdersPage />} />
<Route path="archive" element={<ArchivedOrdersPage />} />
</Route>
</Route>
</Routes>Батьківський компонент OrdersLayout також повинен містити Outlet:
import { NavLink, Outlet } from "react-router-dom";
function OrdersLayout() {
return (
<section>
<h2>Замовлення</h2>
<nav>
<NavLink to=".">Активні</NavLink>{" | "}
<NavLink to="archive">Архів</NavLink>
</nav>
<Outlet />
</section>
);
}Тепер для відображення сторінки архіву React Router виконає таку ієрархію:
DashboardLayout
└── OrdersLayout
└── ArchivedOrdersPageКожен батьківський компонент відповідає за власний Outlet.
Розглянемо різницю між цими посиланнями:
<NavLink to="orders">Замовлення</NavLink>
<NavLink to="/dashboard/orders">Замовлення</NavLink>Перше посилання використовує відносний шлях. Воно додає orders до поточного маршруту.
Друге посилання використовує абсолютний шлях. Воно завжди веде безпосередньо на /dashboard/orders, незалежно від поточного рівня вкладеності.
Для навігації всередині вкладеного розділу зазвичай зручніші відносні шляхи:
<NavLink to="archive">Архів</NavLink>Якщо поточний батьківський маршрут — /dashboard/orders, результатом буде /dashboard/orders/archive.
Для переходу на інший незалежний розділ застосунку доречніше використовувати абсолютний шлях:
<NavLink to="/settings">Загальні налаштування</NavLink>Головна перевага вкладених маршрутів полягає в тому, що спільні елементи не потрібно дублювати в кожному компоненті сторінки.
Без вкладених маршрутів кожна сторінка могла б самостійно містити:
заголовок панелі;
меню розділу;
бічну панель;
додаткові елементи керування.
З Outlet ці елементи описуються один раз у батьківському компоненті:
function DashboardLayout() {
return (
<div>
<aside>
<h2>Меню панелі</h2>
</aside>
<div>
<Outlet />
</div>
</div>
);
}Під час переходу між дочірніми маршрутами DashboardLayout залишається спільною оболонкою, а змінюється лише вміст Outlet.
Для більшості розділів можна використовувати такий шаблон:
function SectionLayout() {
return (
<section>
<h1>Назва розділу</h1>
<nav>
<NavLink to=".">Огляд</NavLink>{" | "}
<NavLink to="details">Деталі</NavLink>
</nav>
<Outlet />
</section>
);
}
function AppRoutes() {
return (
<Routes>
<Route path="section" element={<SectionLayout />}>
<Route index element={<SectionHome />} />
<Route path="details" element={<SectionDetails />} />
</Route>
</Routes>
);
}Цей шаблон складається з трьох частин:
компонент-оболонка з навігацією;
Outlet для дочірнього вмісту;
вкладені Route у конфігурації маршрутів.
Outletfunction DashboardLayout() {
return <h1>Панель керування</h1>;
}Маршрути можуть бути налаштовані правильно, але дочірня сторінка не відобразиться.
Потрібно додати:
function DashboardLayout() {
return (
<>
<h1>Панель керування</h1>
<Outlet />
</>
);
}<Routes>
<Route path="dashboard" element={<DashboardLayout />} />
<Route path="dashboard/orders" element={<OrdersPage />} />
</Routes>Це два незалежні маршрути, а не вкладена структура. OrdersPage не буде відображатися через Outlet у DashboardLayout.
Правильний варіант:
<Routes>
<Route path="dashboard" element={<DashboardLayout />}>
<Route path="orders" element={<OrdersPage />} />
</Route>
</Routes>/ у дочірньому шляху<Route path="dashboard" element={<DashboardLayout />}>
<Route path="/orders" element={<OrdersPage />} />
</Route>Шлях /orders є абсолютним, тому він не є дочірнім шляхом /dashboard.
Потрібно записати шлях без початкового /:
<Route path="dashboard" element={<DashboardLayout />}>
<Route path="orders" element={<OrdersPage />} />
</Route>Якщо для батьківського маршруту немає index-маршруту, то за адресою /dashboard Outlet може залишитися порожнім.
<Route path="dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
</Route>Усередині /dashboard/orders посилання:
<NavLink to="profile">Профіль</NavLink>веде на /dashboard/orders/profile, а не на /dashboard/profile.
Щоб перейти до профілю, потрібно використати абсолютний шлях:
<NavLink to="/dashboard/profile">Профіль</NavLink>Або розмістити це посилання в DashboardLayout, де profile буде правильним відносним шляхом.
Вкладені маршрути описують ієрархію розділів і сторінок застосунку.
Дочірні Route розміщують усередині батьківського Route.
Outlet визначає місце для відображення активного дочірнього маршруту.
Дочірні шляхи зазвичай записують без початкового /.
index визначає сторінку за замовчуванням для батьківського маршруту.
Відносні посилання зручні для навігації всередині вкладеного розділу.
Кожен рівень вкладеності, який має власних дітей, повинен містити Outlet.