Loader API

Loader API — это механизм асинхронной загрузки данных для маршрутов, встроенный в TanStack Router. Он позволяет заранее получать данные до того, как компонент маршрута будет отрендерен, что обеспечивает более предсказуемое поведение приложений и упрощает управление состоянием загрузки.


Основные принципы работы

Loader — это функция, привязанная к маршруту, которая выполняется при переходе на этот маршрут. Она может возвращать данные напрямую или промис, что делает её полностью совместимой с асинхронными операциями:

const route = {
  path: '/user/:id',
  loader: async ({ params }) => {
    const response = await fetch(`/api/users/${params.id}`);
    if (!response.ok) throw new Error('User not found');
    return response.json();
  },
  component: UserComponent
};

Ключевые моменты:

  • Асинхронность: Loader может быть асинхронной функцией.
  • Параметры маршрута: В функцию передается объект с параметрами (params) и другими данными маршрута.
  • Обработка ошибок: Если loader выбрасывает исключение, его можно перехватить через ErrorBoundary или глобальные обработчики маршрутов.
  • Возвращаемое значение: Любые данные, возвращаемые loader, доступны компоненту маршрута через хук useLoaderData.

Использование useLoaderData

После того как loader возвращает данные, компонент маршрута может получить их через хук:

import { useLoaderData } from '@tanstack/router';

function UserComponent() {
  const user = useLoaderData();
  return (
    <div>
      <h1>{user.name}</h1>
      <p>Email: {user.email}</p>
    </div>
  );
}

Особенности:

  • Данные автоматически кэшируются на время существования маршрута.
  • Хук обеспечивает синхронный доступ к уже загруженным данным без дополнительной логики состояния.
  • При переходе между маршрутами с одинаковым loader данные могут быть повторно использованы.

Передача контекста в loader

Loader может получать дополнительный контекст через объект context при конфигурации маршрутизатора. Это удобно для передачи глобальных сервисов, таких как API-клиенты:

const router = createRouter({
  context: {
    apiClient: new ApiClient()
  },
  routes: [
    {
      path: '/posts/:id',
      loader: async ({ params, context }) => {
        return await context.apiClient.fetchPost(params.id);
      },
      component: PostComponent
    }
  ]
});
  • context позволяет разделять логику загрузки данных и конфигурацию API.
  • Использование контекста уменьшает дублирование кода и облегчает тестирование loader.

Обработка ошибок и состояний загрузки

Loader API тесно интегрирован с системой маршрутов для управления ошибками и состоянием загрузки:

  • ErrorBoundary: позволяет перехватывать ошибки загрузки данных на уровне маршрута.
  • pending state: TanStack Router автоматически помечает маршрут как «загружающийся» во время выполнения loader, что позволяет отображать спиннеры или индикаторы прогресса.

Пример использования ErrorBoundary:

const route = {
  path: '/user/:id',
  loader: async ({ params }) => {
    const res = await fetch(`/api/users/${params.id}`);
    if (!res.ok) throw new Error('User not found');
    return res.json();
  },
  component: UserComponent,
  errorComponent: UserErrorComponent
};

Совместное использование loader и маршрутов с вложенностью

Loader API особенно полезен для вложенных маршрутов. Каждый маршрут может иметь свой loader, и TanStack Router обеспечивает правильную последовательность загрузки данных:

const routes = [
  {
    path: '/dashboard',
    loader: fetchDashboard,
    children: [
      {
        path: 'stats',
        loader: fetchStats,
        component: StatsComponent
      },
      {
        path: 'reports',
        loader: fetchReports,
        component: ReportsComponent
      }
    ]
  }
];
  • Последовательность выполнения: родительские loader выполняются перед дочерними.
  • Кэширование данных: родительские данные остаются доступными при переходе между дочерними маршрутами.

Интеграция с Suspense и React Query

Loader API можно использовать совместно с Suspense и TanStack Query для более продвинутой работы с данными:

const route = {
  path: '/todos',
  loader: async () => {
    return queryClient.fetchQuery(['todos'], fetchTodos);
  },
  component: TodosComponent
};
  • Обеспечивает кэширование и синхронизацию данных.
  • Позволяет комбинировать серверные и клиентские данные без дублирования логики.
  • Позволяет Suspense автоматически отображать fallback во время загрузки данных.

Передача параметров и query string

Loader API позволяет работать с query-параметрами:

const route = {
  path: '/search',
  loader: async ({ search }) => {
    const query = search.get('q') || '';
    return fetch(`/api/search?q=${query}`).then(res => res.json());
  },
  component: SearchComponent
};
  • search — объект URLSearchParams, предоставляемый TanStack Router.
  • Обеспечивает реактивное обновление данных при изменении query-параметров.

Loader API в TanStack Router обеспечивает мощный и гибкий инструмент для управления асинхронными данными в маршрутах, минимизируя необходимость ручного управления состояниями загрузки, ошибок и кэширования. Это делает архитектуру приложения более чистой, предсказуемой и легко масштабируемой.