React Router является основным инструментом для организации маршрутизации в приложениях на React, однако неправильная работа с ним может привести к избыточным ререндерам компонентов и падению производительности. Важно понимать, как маршруты взаимодействуют с React-компонентами и как минимизировать ненужные обновления.
Все компоненты, использующие useLocation,
useParams, useNavigate или
useMatch, получают доступ к контексту маршрутизатора. Любое
изменение состояния маршрута вызывает обновление этого контекста.
Следовательно:
Пример:
import { useLocation } from "react-router-dom";
function Header() {
const location = useLocation();
console.log("Header перерендерился", location.pathname);
return <h1>Текущий путь: {location.pathname}</h1>;
}
Даже если изменения маршрута не затрагивают визуально
Header, использование useLocation заставляет
его ререндериться при каждом изменении пути.
React Router поддерживает ленивую загрузку компонентов с помощью
React.lazy и Suspense, что уменьшает нагрузку
на первоначальный рендер и ускоряет загрузку страниц. Например:
import { lazy, Suspense } from "react";
import { Route, Routes } from "react-router-dom";
const Dashboard = lazy(() => import("./Dashboard"));
const Profile = lazy(() => import("./Profile"));
function AppRoutes() {
return (
<Suspense fallback={<div>Загрузка...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Suspense>
);
}
Lazy-loading помогает избегать рендеринга тяжелых компонентов на страницах, где они не нужны, тем самым снижая избыточные обновления.
Использование вложенных маршрутов через Outlet позволяет
разделять части интерфейса на независимые блоки. Это особенно важно для
страниц с общей навигацией или боковыми панелями:
function Layout() {
return (
<div>
<Sidebar />
<main>
<Outlet />
</main>
</div>
);
}
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route path="dashboard" element={<Dashboard />} />
<Route path="profile" element={<Profile />} />
</Route>
</Routes>
);
}
Преимущество заключается в том, что изменение внутреннего маршрута
(dashboard или profile) не заставляет
ререндериться Sidebar, так как она находится вне
Outlet.
React.memo для маршрутовКомпоненты, которые рендерятся внутри маршрутов и не зависят от
параметров пути, можно оборачивать в React.memo:
const Sidebar = React.memo(function Sidebar() {
console.log("Sidebar ререндерится только при изменении пропсов");
return <div>Меню</div>;
});
Это предотвращает повторные рендеры при навигации между маршрутами, если пропсы компонента остаются неизменными.
useMatch и селективного реагирования
на URLВместо useLocation, который подписывает компонент на все
изменения маршрута, можно использовать useMatch, чтобы
реагировать только на конкретные пути:
import { useMatch } from "react-router-dom";
function ActiveLink() {
const match = useMatch("/dashboard");
return <div>{match ? "Вы на Dashboard" : "Не на Dashboard"}</div>;
}
useMatch перерендеривает компонент только при совпадении
конкретного маршрута, снижая количество лишних обновлений.
useLocation и
useParams в компонентах, которые не зависят от
всех изменений маршрута.Outlet, чтобы ререндерить только изменяющуюся
часть страницы.React.memo для
компонентов, которые не зависят от состояния маршрута.useMatch, вместо глобальных подписок на весь путь.Эти подходы позволяют существенно улучшить производительность приложений на React с React Router, особенно на крупных проектах с множеством маршрутов и сложной навигацией.