Loader в TanStack Router — это асинхронная функция, которая
выполняется перед рендерингом компонента маршрута. Она используется для
загрузки данных, необходимых для отображения страницы, и может
возвращать любые значения, которые будут доступны в компоненте через хук
useLoaderData. Основная цель loader’ов — централизованное
управление данными маршрутов и предотвращение состояния “loading” в
самих компонентах.
Пример базового loader’а:
import { createRouter, Route } from '@tanstack/router'
const userLoader = async ({ params }) => {
const response = await fetch(`/api/users/${params.userId}`)
if (!response.ok) throw new Error('User not found')
return response.json()
}
const router = createRouter({
routes: [
new Route({
path: '/users/:userId',
component: UserComponent,
loader: userLoader,
}),
],
})
Loader автоматически вызывается при переходе на маршрут и может быть повторно использован в дочерних маршрутах через механизм наследования данных.
1. Кэширование данных
Асинхронные запросы в loader’ах могут быть дорогими по времени и нагрузке на сервер. Для уменьшения количества запросов используется кэширование:
useLoaderData. Можно хранить загруженные данные в
глобальном состоянии и проверять наличие данных перед выполнением
запроса.const userLoader = async ({ params, context }) => {
if (context.userCache[params.userId]) {
return context.userCache[params.userId]
}
const response = await fetch(`/api/users/${params.userId}`)
const data = await response.json()
context.userCache[params.userId] = data
return data
}
2. Ленивая загрузка данных (Lazy Loading)
Для больших страниц с множеством данных можно разделить loader на несколько частей:
Пример разделения:
const mainLoader = async ({ params }) => {
return fetch(`/api/posts/${params.postId}`).then(res => res.json())
}
const commentsLoader = async ({ params }) => {
return fetch(`/api/posts/${params.postId}/comments`).then(res => res.json())
}
// mainLoader вызывается сразу, commentsLoader только при рендере компонента комментариев
3. Параллельная загрузка данных
Если компонент требует несколько независимых источников данных, их
лучше загружать параллельно через Promise.all, чтобы
сократить общее время ожидания:
const dashboardLoader = async () => {
const [users, posts, notifications] = await Promise.all([
fetch('/api/users').then(res => res.json()),
fetch('/api/posts').then(res => res.json()),
fetch('/api/notifications').then(res => res.json())
])
return { users, posts, notifications }
}
4. Дедупликация запросов
При переходе между маршрутами, использующими одинаковые loader’ы, можно избежать повторного запроса:
const deduplicatedLoader = async ({ params, context }) => {
const cacheKey = `user-${params.userId}`
if (context.cache[cacheKey]) return context.cache[cacheKey]
const data = await fetch(`/api/users/${params.userId}`).then(res => res.json())
context.cache[cacheKey] = data
return data
}
5. Обработка ошибок и fallback
Loader может выбрасывать ошибки, которые затем обрабатываются через
ErrorBoundary. Для оптимизации полезно:
6. Интеграция с Suspense
Loader’ы TanStack Router могут работать с React Suspense для отображения индикаторов загрузки:
<Suspense fallback={<Loading />}>
<RouteComponent />
</Suspense>
Оптимизация заключается в минимизации времени ожидания критических данных и переносе второстепенной информации в отдельные Suspense boundaries.
7. Использование контекста и инъекций зависимостей
Для повторного использования loader’ов и передачи общих данных используют контексты:
const DataContext = React.createContext({})
const contextLoader = async ({ context }) => {
if (!context.apiClient) context.apiClient = new ApiClient()
return context.apiClient.getData()
}
Такой подход снижает количество глобальных зависимостей и улучшает повторное использование loader’ов.
8. Лимитирование количества запросов
При работе с списками данных с пагинацией полезно ограничивать количество параллельных запросов, используя throttling или batching. Это уменьшает нагрузку на API и ускоряет реакцию интерфейса.
const batchedLoader = async ({ params }) => {
const batch = params.ids.slice(0, 10) // берем только первые 10
return Promise.all(batch.map(id => fetch(`/api/item/${id}`).then(res => res.json())))
}
Выводы по оптимизации loader’ов
Эти методы в комбинации делают работу loader’ов TanStack Router эффективной даже для больших, сложных приложений с множеством асинхронных зависимостей.