React Router предоставляет возможность организовывать маршруты приложения так, чтобы одни компоненты рендерились внутри других. Это называется вложенными layout и является ключевым инструментом для построения сложных интерфейсов с повторяющимися структурами, например, боковыми панелями, навигацией или хедером.
В React Router вложенные маршруты создаются с помощью компонента
<Outlet>. Этот компонент указывает, где внутри
родительского layout будут рендериться дочерние маршруты. Простейший
пример:
import { BrowserRouter, Routes, Route, Outlet } from 'react-router-dom';
function DashboardLayout() {
return (
<div>
<header>Панель навигации</header>
<main>
<Outlet />
</main>
</div>
);
}
function Overview() {
return <h2>Обзор</h2>;
}
function Reports() {
return <h2>Отчёты</h2>;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<Overview />} />
<Route path="reports" element={<Reports />} />
</Route>
</Routes>
</BrowserRouter>
);
}
Здесь DashboardLayout является родительским layout, а
<Outlet /> рендерит дочерние маршруты
/dashboard и /dashboard/reports. Использование
index задаёт маршрут по умолчанию внутри родителя.
React Router поддерживает неограниченное количество уровней вложенности. Можно создавать вложенные layout внутри других layout. Пример:
function AdminLayout() {
return (
<div>
<nav>Админ панель</nav>
<section>
<Outlet />
</section>
</div>
);
}
function UsersLayout() {
return (
<div>
<h3>Пользователи</h3>
<Outlet />
</div>
);
}
function UserList() {
return <p>Список пользователей</p>;
}
function UserDetail() {
return <p>Детали пользователя</p>;
}
<Routes>
<Route path="/admin" element={<AdminLayout />}>
<Route path="users" element={<UsersLayout />}>
<Route index element={<UserList />} />
<Route path=":id" element={<UserDetail />} />
</Route>
</Route>
</Routes>
В этом примере структура /admin/users/:id рендерится
внутри UsersLayout, который сам находится в
AdminLayout. Таким образом можно создавать
иерархические интерфейсы, не дублируя код навигации или
хедера.
<Outlet> с контекстомДля передачи данных из родительского layout в дочерние маршруты удобно использовать React Context. Пример:
import { createContext, useContext } from 'react';
const UserContext = createContext();
function UsersLayout() {
const users = ['Alice', 'Bob', 'Charlie'];
return (
<UserContext.Provider value={users}>
<h2>Пользователи</h2>
<Outlet />
</UserContext.Provider>
);
}
function UserList() {
const users = useContext(UserContext);
return (
<ul>
{users.map(user => <li key={user}>{user}</li>)}
</ul>
);
}
Дочерние маршруты автоматически получают доступ к данным через контекст, что делает layout мощным инструментом для централизованного управления состоянием на уровне маршрутов.
React Router позволяет комбинировать динамические маршруты с layout. Например:
function BlogLayout() {
return (
<div>
<header>Блог</header>
<Outlet />
</div>
);
}
function BlogList() {
return <p>Список постов</p>;
}
function BlogPost() {
const { postId } = useParams();
return <p>Пост с ID: {postId}</p>;
}
<Routes>
<Route path="/blog" element={<BlogLayout />}>
<Route index element={<BlogList />} />
<Route path=":postId" element={<BlogPost />} />
</Route>
</Routes>
Здесь :postId динамически рендерится внутри
родительского layout BlogLayout. Такой подход позволяет
разделять общий интерфейс и контентные блоки без повторного кода.
<Outlet> только один раз в
родительском layout для предотвращения ошибок рендеринга.AdminLayout, UsersLayout,
BlogLayout, чтобы повторное использование компонентов было
максимально эффективным.Вложенные layout в React Router делают структуру приложения модульной и предсказуемой, упрощают управление общими элементами интерфейса и создают удобную иерархию маршрутов.