Уменьшение размера loader данных

В React Router (v6 и выше) загрузка данных с помощью loader стала стандартным способом асинхронного получения информации до отображения маршрута. Основной проблемой при использовании loader в крупных приложениях является избыточная загрузка данных и потенциальное замедление рендера. Для оптимизации важно применять подходы, уменьшающие объём передаваемых данных и ускоряющие рендеринг компонентов.


Использование селективных запросов в loader

При работе с API часто запрашиваются объёмные объекты. Вместо того чтобы загружать весь ресурс, имеет смысл выбирать только нужные поля. Например:

export async function loader({ params }) {
  const response = await fetch(`/api/users/${params.userId}?fields=id,name,email`);
  if (!response.ok) {
    throw new Response("Failed to load user", { status: response.status });
  }
  return response.json();
}

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

  • Параметр fields позволяет ограничить объём данных.
  • Снижается нагрузка на сеть и ускоряется рендер маршрута.
  • Уменьшение размера данных в loader напрямую влияет на скорость initial load страницы.

Lazy-загрузка вложенных данных

Если маршрут содержит подкомпоненты с собственными loader-ами, имеет смысл выполнять lazy-загрузку для отдельных частей страницы:

export const routeConfig = {
  path: "/dashboard",
  element: <Dashboard />,
  loader: async () => {
    return fetch("/api/dashboard-summary").then(res => res.json());
  },
  children: [
    {
      path: "stats",
      element: <Stats />,
      loader: async () => fetch("/api/dashboard-stats").then(res => res.json())
    }
  ]
};

Особенности подхода:

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

Использование defer для частичной загрузки данных

React Router поддерживает defer, позволяющий возвращать часть данных сразу, а остальную асинхронно:

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

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

Принцип работы:

  • Компонент получает доступ к user сразу после рендеринга, а уведомления подгружаются асинхронно.
  • Компонент может использовать Suspense для отображения placeholders до полной загрузки.
  • Снижается время initial render и улучшается пользовательский опыт.

Кэширование данных loader

Для уменьшения количества сетевых запросов имеет смысл кэшировать результаты loader:

const cache = new Map();

export async function loader({ params }) {
  if (cache.has(params.userId)) {
    return cache.get(params.userId);
  }
  const response = await fetch(`/api/users/${params.userId}`);
  const data = await response.json();
  cache.set(params.userId, data);
  return data;
}

Преимущества:

  • Повторные переходы к маршруту не инициируют новые запросы.
  • Уменьшается нагрузка на сервер.
  • Положительно сказывается на производительности крупных приложений.

Разделение загрузки данных на страницы (pagination)

Если ресурс большой, можно загружать данные частями:

export async function loader({ request }) {
  const url = new URL(request.url);
  const page = url.searchParams.get("page") || 1;
  return fetch(`/api/posts?page=${page}&limit=20`).then(res => res.json());
}

Эффект:

  • Передается только необходимый объём данных для текущей страницы.
  • Уменьшается задержка initial load.
  • Пользователь получает быстрый отклик и может динамически подгружать дополнительные страницы.

Заключение по подходам

Для уменьшения размера данных loader в React Router важно сочетать:

  • Селективный запрос данных, чтобы получать только нужное.
  • Lazy-загрузку дочерних маршрутов.
  • Использование defer для частичной асинхронной загрузки.
  • Кэширование для минимизации повторных запросов.
  • Постраничную загрузку для больших коллекций.

Эти методы позволяют создавать быстрые, отзывчивые интерфейсы даже при большом объёме данных и сложной маршрутизации.