8. Вложенные маршруты
Вложенные маршруты — одна из самых мощных возможностей Remix. Они позволяют создавать сложные UI с несколькими уровнями layouts, при этом каждый уровень загружает свои данные параллельно.
Outlet — точка монтирования
Заголовок раздела «Outlet — точка монтирования»// app/routes/dashboard.tsx — родительский маршрутimport { Outlet, NavLink } from "@remix-run/react";
export async function loader() { // Данные для этого уровня const user = await getCurrentUser(); return json({ user });}
export default function Dashboard() { const { user } = useLoaderData<typeof loader>();
return ( <div className="dashboard"> <nav> <p>Привет, {user.name}!</p> <NavLink to="/dashboard">Главная</NavLink> <NavLink to="/dashboard/stats">Статистика</NavLink> <NavLink to="/dashboard/settings">Настройки</NavLink> </nav> <main> {/* Здесь рендерятся дочерние маршруты */} <Outlet /> </main> </div> );}Структура файлов
Заголовок раздела «Структура файлов»app/routes/ dashboard.tsx → /dashboard/* (layout) dashboard._index.tsx → /dashboard dashboard.stats.tsx → /dashboard/stats dashboard.settings.tsx → /dashboard/settings dashboard.users.$id.tsx → /dashboard/users/:idПараллельная загрузка данных
Заголовок раздела «Параллельная загрузка данных»Remix загружает данные всех вложенных маршрутов параллельно:
GET /dashboard/stats
Параллельно выполняются: ├── dashboard.tsx loader() → getCurrentUser() └── dashboard.stats.tsx loader() → getStats()
Результат: самый медленный из двух определяет время загрузки(а не их сумма, как было бы при последовательной загрузке!)Передача данных родитель → дочерний
Заголовок раздела «Передача данных родитель → дочерний»Через useRouteLoaderData:
// В дочернем маршруте dashboard.stats.tsximport { useRouteLoaderData } from "@remix-run/react";import type { loader as dashboardLoader } from "./dashboard";
export default function Stats() { // Получаем данные из родительского loader const dashData = useRouteLoaderData<typeof dashboardLoader>("routes/dashboard"); const { stats } = useLoaderData<typeof loader>();
return ( <div> <h2>Привет, {dashData?.user.name}!</h2> <p>Ваша статистика...</p> </div> );}Контекст через Outlet
Заголовок раздела «Контекст через Outlet»// Родительский маршрутexport default function Parent() { const data = useLoaderData(); return ( <div> <Outlet context={{ theme: "dark", user: data.user }} /> </div> );}
// Дочерний маршрутimport { useOutletContext } from "@remix-run/react";
export default function Child() { const { theme, user } = useOutletContext<{ theme: string; user: User }>(); return <p>Тема: {theme}, Пользователь: {user.name}</p>;}