В TanStack Router, layouts (макеты) выполняют роль контейнеров для дочерних маршрутов и позволяют управлять общим состоянием или визуальной структурой приложения. Одним из ключевых аспектов использования layouts является передача данных между уровнями маршрутов. Важно понимать, что layouts могут как получать данные из родительских маршрутов, так и передавать их дочерним.
Для передачи данных layout использует свойство
loader, которое позволяет загружать асинхронные
данные перед рендерингом компонента. В TanStack Router loader возвращает
объект, который доступен всем дочерним маршрутам через контекст
маршрутизатора.
import { createRouter, createRoute } from '@tanstack/router';
const layoutRoute = createRoute({
path: '/dashboard',
component: DashboardLayout,
loader: async () => {
const userData = await fetch('/api/user').then(res => res.json());
return { userData };
},
children: [
// дочерние маршруты
],
});
Ключевые моменты:
loader вызывается до рендеринга
компонента layout.useLoaderData().Дочерние маршруты могут использовать данные layout без необходимости повторной загрузки:
import { useLoaderData } from '@tanstack/router';
function UserProfile() {
const { userData } = useLoaderData({ from: 'dashboard' });
return (
{userData.name}
{userData.email}
);
}
Особенности работы:
useLoaderData({ from: 'layoutId' }) позволяет указать
конкретный layout, из которого требуется взять данные.Layouts могут использовать параметры URL, определённые на их уровне, для загрузки данных:
const layoutRoute = createRoute({
path: '/projects/:projectId',
component: ProjectLayout,
loader: async ({ params }) => {
const project = await fetch(`/api/projects/${params.projectId}`).then(r => r.json());
return { project };
},
children: [
{ path: 'tasks', component: ProjectTasks },
],
});
params в loader.TanStack Router также поддерживает передачу дополнительного контекста через layout:
import { Outlet, useMatch } from '@tanstack/router';
function ProjectLayout() {
const match = useMatch('/projects/:projectId');
const contextValue = {
projectId: match.params.projectId,
permissions: ['read', 'write'],
};
return (
);
}
Часто используется комбинация loader и context, чтобы добавить гибкость и контроль над данными:
function ProjectLayout() {
const { project } = useLoaderData();
const permissions = ['read', 'write']; // статические данные
return (
);
}
В сложных приложениях могут использоваться вложенные layouts, где данные одного layout становятся входными для следующего:
const rootRoute = createRoute({
path: '/',
component: RootLayout,
loader: () => ({ theme: 'dark' }),
children: [
createRoute({
path: 'dashboard',
component: DashboardLayout,
loader: ({ parentData }) => ({
...parentData,
notifications: fetchNotifications(),
}),
}),
],
});
parentData содержит данные из родительского
loader.