Создание функций loader

Функции loader в React Router используются для предварительной загрузки данных перед рендерингом компонента маршрута. Это позволяет компонента сразу получать все необходимые данные, не полагаясь на последующий useEffect или асинхронные вызовы внутри компонента.

Синтаксис создания функции loader:

import { json } from "react-router-dom";

export async function exampleLoader({ params, request }) {
  const response = await fetch(`/api/items/${params.id}`);
  if (!response.ok) {
    throw json({ message: "Ошибка при загрузке данных" }, { status: 500 });
  }
  const data = await response.json();
  return data;
}

Параметры функции:

  • params — объект с параметрами маршрута, например id из URL /items/:id.
  • request — объект Request аналогичный стандартному Fetch API, содержащий информацию о запросе.
  • context — опциональный объект, доступный в маршрутизаторе через RouterProvider.

Возвращаемые значения:

Функция может возвращать:

  • Любой JavaScript объект или массив — они станут доступными через хук useLoaderData в компоненте маршрута.
  • Промис с объектом данных.
  • Объект с функцией redirect или ошибкой через throw json() для обработки ошибок маршрута.

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

После того как функция loader возвращает данные, они становятся доступными в компоненте с помощью хука useLoaderData:

import { useLoaderData } from "react-router-dom";

function ItemDetail() {
  const item = useLoaderData();
  return (
    <div>
      <h2>{item.name}</h2>
      <p>{item.description}</p>
    </div>
  );
}

useLoaderData гарантирует, что компонент получит все данные синхронно, когда маршрутизатор завершит выполнение loader.


Асинхронные операции и обработка ошибок

Функции loader отлично подходят для работы с асинхронными запросами, такими как fetch, запросы к GraphQL или обращения к базе данных.

Пример с обработкой ошибок:

import { redirect, json } from "react-router-dom";

export async function protectedLoader({ request }) {
  const token = localStorage.getItem("authToken");
  if (!token) {
    return redirect("/login");
  }

  const response = await fetch("/api/protected", {
    headers: { Authorization: `Bearer ${token}` },
  });

  if (!response.ok) {
    throw json({ message: "Доступ запрещен" }, { status: 403 });
  }

  return response.json();
}

Здесь показано:

  • Как перенаправить пользователя, если условия не выполнены (redirect).
  • Как выбросить ошибку с кодом и сообщением (throw json()), которую можно отловить через ErrorBoundary маршрута.

Работа с параметрами маршрута

Функции loader имеют доступ к динамическим сегментам URL через params.

export async function userLoader({ params }) {
  const response = await fetch(`/api/users/${params.userId}`);
  return response.json();
}

Если маршрут определен как /users/:userId, params.userId будет содержать значение из URL.


Передача данных через context

Контекст позволяет передавать глобальные объекты или функции в loader без импорта напрямую в каждый модуль.

// App.js
import { createBrowserRouter, RouterProvider } from "react-router-dom";

const router = createBrowserRouter(routes, { context: { apiClient } });

export default function App() {
  return <RouterProvider router={router} />;
}

// loader.js
export async function someLoader({ context }) {
  const data = await context.apiClient.getData();
  return data;
}

Асинхронная маршрутизация и defer

React Router 6.4+ позволяет использовать defer, чтобы частично загружать данные:

import { defer } from "react-router-dom";

export function deferredLoader() {
  return defer({
    user: fetch("/api/user").then(res => res.json()),
    posts: fetch("/api/posts").then(res => res.json()),
  });
}

Компонент может использовать useLoaderData и работать с асинхронными свойствами через <Suspense>:

const data = useLoaderData();

<Suspense fallback={<p>Загрузка...</p>}>
  <Await resolve={data.posts}>
    {(posts) => <PostsList posts={posts} />}
  </Await>
</Suspense>

defer помогает не блокировать рендер всего маршрута, если часть данных загружается медленно.


Интеграция с ErrorBoundary и редиректами

Ошибки и редиректы в loader можно обрабатывать на уровне маршрута, используя специальные свойства errorElement и loader вместе с redirect.

const router = createBrowserRouter([
  {
    path: "/dashboard",
    loader: dashboardLoader,
    element: <Dashboard />,
    errorElement: <DashboardError />,
  },
]);
  • loader может выбросить ошибку или сделать redirect.
  • errorElement автоматически отобразится при ошибке, без необходимости обрабатывать её внутри компонента.

Практические советы

  • Разделять логику загрузки данных и компонент для упрощения тестирования.
  • Использовать json() для формирования ошибок с кодом статуса.
  • Применять defer при загрузке больших данных, чтобы не блокировать UI.
  • Проверять параметры params и request для предотвращения некорректных запросов.
  • Грамотно использовать context для передачи общих сервисов, таких как API-клиенты или настройки авторизации.

Функции loader становятся ядром современной маршрутизации в React Router, позволяя строить реактивные приложения с предзагрузкой данных и минимальной зависимостью от эффекта рендеринга.