В современных SPA-приложениях, построенных с использованием React Router, кеширование результатов запросов и данных маршрутов является важным инструментом для оптимизации производительности и улучшения пользовательского опыта. React Router предоставляет ряд встроенных механизмов и интеграцию с внешними библиотеками для эффективного управления кешированием.
React Router использует концепцию loader-ов для
предварительной загрузки данных маршрутов. Каждый маршрут может
определять функцию loader, которая вызывается перед
рендерингом компонента и возвращает данные, необходимые для отображения
страницы.
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import UserPage from "./UserPage";
async function userLoader({ params }) {
const response = await fetch(`/api/users/${params.userId}`);
return response.json();
}
const router = createBrowserRouter([
{
path: "/users/:userId",
element: <UserPage />,
loader: userLoader
}
]);
export default function App() {
return <RouterProvider router={router} />;
}
Loader функции можно комбинировать с клиентским кешированием для предотвращения повторных запросов. Одним из простых подходов является использование Map или WeakMap для хранения результатов:
const cache = new Map();
async function cachedUserLoader({ params }) {
const key = params.userId;
if (cache.has(key)) {
return cache.get(key);
}
const response = await fetch(`/api/users/${key}`);
const data = await response.json();
cache.set(key, data);
return data;
}
Такой подход гарантирует, что при повторном переходе на тот же маршрут данные будут взяты из кеша, что снижает нагрузку на сервер и ускоряет рендеринг.
defer и ленивой загрузки данныхReact Router поддерживает функцию defer, позволяющую
лениво загружать данные и кешировать результаты асинхронных
операций.
import { defer, Await } from "react-router-dom";
async function postsLoader() {
const response = await fetch("/api/posts");
return defer({ posts: response.json() });
}
function Posts() {
const data = useLoaderData();
return (
<Suspense fallback={<div>Загрузка...</div>}>
<Await resolve={data.posts}>
{(posts) => (
<ul>
{posts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
)}
</Await>
</Suspense>
);
}
Особенности:
defer позволяет рендерить компонент до того, как все
данные загружены.1. Локальный кеш на клиенте
Map, WeakMap или стейт React
(useState, useReducer).2. Сторонние библиотеки
import { useQuery } from "@tanstack/react-query";
function User({ userId }) {
const { data } = useQuery(["user", userId], () =>
fetch(`/api/users/${userId}`).then(res => res.json())
);
return <div>{data.name}</div>;
}
3. Кеширование на уровне HTTP
Cache-Control на сервере.При работе с кешем важно учитывать устаревание данных:
{ data, timestamp }.const cache = new Map();
const TTL = 5 * 60 * 1000; // 5 минут
async function timedUserLoader({ params }) {
const key = params.userId;
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < TTL) {
return cached.data;
}
const response = await fetch(`/api/users/${key}`);
const data = await response.json();
cache.set(key, { data, timestamp: Date.now() });
return data;
}
React Router предоставляет хук
useNavigation, который позволяет
отслеживать переходы и оптимизировать кеширование данных между
страницами:
import { useNavigation, useLoaderData } from "react-router-dom";
function Dashboard() {
const navigation = useNavigation();
const data = useLoaderData();
if (navigation.state === "loading") {
return <div>Предзагрузка данных...</div>;
}
return <div>{data.summary}</div>;
}
defer с локальным или внешним кешем ускоряет
отображение.Map для больших объемов данных.Эти подходы обеспечивают эффективное управление данными маршрутов, снижают количество сетевых запросов и повышают отзывчивость приложений на React Router.