При работе с React Router важно правильно организовать навигацию и отображение длинных страниц, чтобы избежать проблем с производительностью, пользовательским опытом и управлением состоянием. Рассмотрим основные подходы и практики.
Длинные страницы часто включают множество компонентов и данных,
которые не нужно загружать сразу. Для оптимизации можно использовать
ленивую загрузку компонентов через React.lazy и
Suspense.
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
const LongPage = lazy(() => import('./LongPage'));
const HomePage = lazy(() => import('./HomePage'));
function App() {
return (
<Router>
<Suspense fallback={<div>Загрузка...</div>}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/long" element={<LongPage />} />
</Routes>
</Suspense>
</Router>
);
}
Ключевой момент: ленивые компоненты загружаются только при переходе на соответствующий маршрут, что уменьшает размер основного бандла и ускоряет старт приложения.
Для длинных страниц часто требуется большое количество данных. React
Router предоставляет возможность загружать данные асинхронно через
loader (начиная с версии 6.4).
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import LongPage from './LongPage';
async function loadLongPageData() {
const response = await fetch('/api/longpage');
return response.json();
}
const router = createBrowserRouter([
{
path: "/long",
element: <LongPage />,
loader: loadLongPageData,
},
]);
function App() {
return <RouterProvider router={router} />;
}
Особенность: загрузка данных привязывается к маршруту, что упрощает управление состоянием и делает переходы более предсказуемыми.
Длинные страницы удобно делить на логические секции с отдельными маршрутизируемыми подстраницами. Это улучшает навигацию и позволяет использовать ленивую подгрузку на уровне секций.
import { Routes, Route, Outlet } from 'react-router-dom';
function LongPage() {
return (
<div>
<h1>Длинная страница</h1>
<Routes>
<Route path="section1" element={<Section1 />} />
<Route path="section2" element={<Section2 />} />
</Routes>
<Outlet />
</div>
);
}
Принцип: каждая секция может быть отдельным компонентом с собственной логикой, что облегчает поддержку и тестирование.
При переходе между секциями длинной страницы важно управлять
положением скролла. React Router не делает этого автоматически, поэтому
часто используют эффект с useEffect.
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
Встраивание компонента в приложение гарантирует, что при каждом
переходе страница будет прокручиваться в начало. Для якорей можно
использовать метод element.scrollIntoView():
function navigateToAnchor(id) {
const element = document.getElementById(id);
if (element) element.scrollIntoView({ beh * avior: 'smooth' });
}
Если страница содержит длинные списки или таблицы, стандартная
отрисовка всех элементов одновременно может замедлить приложение.
Библиотеки типа react-window или
react-virtualized позволяют рендерить только видимую часть
контента:
import { FixedSizeList as List } from 'react-window';
function LargeList({ items }) {
return (
<List
height={600}
itemCount={items.length}
itemSize={50}
width={300}
>
{({ index, style }) => (
<div style={style}>{items[index]}</div>
)}
</List>
);
}
Эффект: повышается производительность, особенно при динамическом обновлении данных или прокрутке длинных страниц.
Для длинных страниц важно сохранять состояние при возврате назад,
чтобы пользователь не терял позицию и данные. Используют
React Router вместе с глобальным состоянием
(Redux, Zustand) или локальными хранилищами
(sessionStorage):
import { useState, useEffect } from 'react';
function useCachedData(key, fetcher) {
const [data, setData] = useState(() => {
const saved = sessionStorage.getItem(key);
return saved ? JSON.parse(saved) : null;
});
useEffect(() => {
if (!data) {
fetcher().then(result => {
setData(result);
sessionStorage.setItem(key, JSON.stringify(result));
});
}
}, [data, fetcher, key]);
return data;
}
Вывод: пользовательский опыт значительно улучшается, так как возвращение к странице не требует повторной загрузки всего контента.
Для длинных страниц с подстраницами можно использовать состояние в маршрутах:
import { useNavigate } from 'react-router-dom';
function navigateToSection(section) {
navigate(`/long#${section}`, { replace: false, state: { scrollPosition: window.scrollY } });
}
При возврате к странице можно восстановить позицию скролла из состояния маршрута.
Эти методы позволяют создавать большие, структурированные и отзывчивые страницы в React Router, обеспечивая быструю работу и удобство для пользователя.