Хук useLoaderData

Хук useLoaderData является одним из ключевых инструментов для работы с асинхронными данными в приложениях на React с использованием React Router. Он позволяет компонентам получать данные, предварительно загруженные в маршрутах, без необходимости вручную управлять состоянием загрузки или эффектами.

Основная концепция

useLoaderData используется в сочетании с функцией loader, которая определяется на уровне маршрута. Функция loader выполняется до рендера компонента, привязанного к этому маршруту, что позволяет:

  • Загружать данные заранее;
  • Управлять редиректами и ошибками до отображения компонента;
  • Исключить необходимость использования useEffect для первичной загрузки данных.

Синтаксис

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

function Profile() {
  const data = useLoaderData();
  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.email}</p>
    </div>
  );
}

В этом примере data — это объект, возвращённый функцией loader для маршрута, на котором рендерится компонент Profile.

Настройка функции loader

Функция loader определяется при настройке маршрута:

import { createBrowserRouter } from "react-router-dom";
import Profile from "./Profile";

const router = createBrowserRouter([
  {
    path: "/profile/:userId",
    element: <Profile />,
    loader: async ({ params }) => {
      const response = await fetch(`/api/users/${params.userId}`);
      if (!response.ok) {
        throw new Response("User not found", { status: 404 });
      }
      return response.json();
    },
  },
]);

Особенности функции loader:

  • Получает объект с ключами params, request и context.
  • Может возвращать данные любого типа: объекты, массивы, строки.
  • Может выбрасывать Response для обработки ошибок на уровне маршрута.

Интеграция с параметрами маршрута

useLoaderData автоматически связывает данные с текущим маршрутом, включая динамические параметры:

const { userId } = useParams(); // использование для других целей
const user = useLoaderData();   // данные, загруженные loader

loader получает параметры маршрута через params, что упрощает работу с динамическими URL:

loader: async ({ params }) => {
  return fetch(`/api/posts/${params.postId}`).then(res => res.json());
}

Обработка ошибок и редиректов

Функция loader может бросать исключения или возвращать специальные объекты для редиректа:

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

loader: async ({ params }) => {
  const response = await fetch(`/api/items/${params.id}`);
  if (response.status === 404) {
    return redirect("/not-found");
  }
  return response.json();
}

Компонент, использующий useLoaderData, получает либо данные, либо маршрут не будет отображен, если выполнен редирект.

Асинхронная загрузка и Suspense

useLoaderData работает синхронно в компоненте, но данные фактически загружаются асинхронно до рендера маршрута. Это позволяет интегрировать React Router с Suspense для отображения индикатора загрузки:

import { Await, useLoaderData } from "react-router-dom";
import { Suspense } from "react";

function Dashboard() {
  const dataPromise = useLoaderData();

  return (
    <Suspense fallback={<div>Загрузка данных...</div>}>
      <Await resolve={dataPromise}>
        {(data) => (
          <div>
            <h2>{data.title}</h2>
            <p>{data.description}</p>
          </div>
        )}
      </Await>
    </Suspense>
  );
}

Это позволяет обрабатывать промисы непосредственно в компоненте без создания отдельного состояния загрузки через useState и useEffect.

Контекстные данные loader

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

const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    loader: async () => ({ theme: "dark" }),
    children: [
      {
        path: "settings",
        element: <Settings />,
      },
    ],
  },
]);

function Settings() {
  const { theme } = useLoaderData(); // наследует данные родительского loader
  return <div>Текущая тема: {theme}</div>;
}

Преимущества использования useLoaderData

  • Упрощение управления данными: нет необходимости в дополнительных эффектах и состояниях;
  • Синхронизация загрузки данных с маршрутизацией: компонент рендерится только после успешной загрузки данных;
  • Поддержка асинхронных операций, ошибок и редиректов на уровне маршрутов;
  • Лёгкая интеграция с Suspense и отложенной загрузкой.

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

  • Использовать useLoaderData только для данных, необходимых для первоначального рендера маршрута;
  • Для динамически обновляемых данных после рендера использовать отдельный хук useEffect или state management;
  • Ловить ошибки в loader с помощью throw new Response или собственного обработчика ошибок маршрута;
  • Не смешивать useLoaderData с fetch внутри компонента, чтобы избежать дублирования загрузки данных.

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