При разработке приложений на React с использованием React Router часто возникает необходимость отображать индикаторы загрузки при асинхронной подгрузке компонентов или данных. Это особенно важно для маршрутов, где контент загружается динамически с сервера. React Router предоставляет несколько инструментов и паттернов для решения этой задачи.
Suspense и ленивой загрузки компонентовДля ленивой загрузки компонентов применяется функция
React.lazy, которая позволяет динамически импортировать
компоненты только при необходимости. В сочетании с компонентом
Suspense можно отображать индикатор загрузки, пока
компонент подгружается.
import { Suspense, lazy } from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
const Home = lazy(() => import("./Home"));
const About = lazy(() => import("./About"));
function App() {
return (
<Router>
<Suspense fallback={<div>Загрузка...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
);
}
Ключевые моменты:
fallback внутри Suspense определяет
компонент, отображаемый во время загрузки.loader в React Router v6.4+Начиная с версии 6.4, React Router поддерживает концепцию loaders — функций для асинхронного получения данных перед рендером маршрута. Это позволяет интегрировать индикаторы загрузки непосредственно в маршрутизацию.
import { createBrowserRouter, RouterProvider } from "react-router-dom";
async function homeLoader() {
const response = await fetch("/api/home");
return response.json();
}
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
loader: homeLoader,
},
]);
function App() {
return <RouterProvider router={router} />;
}
Чтобы показывать индикатор загрузки во время выполнения
loader, применяют компонент useNavigation:
import { useNavigation } from "react-router-dom";
function AppLayout({ children }) {
const navigation = useNavigation();
return (
<div>
{navigation.state === "loading" && <div>Загрузка данных...</div>}
{children}
</div>
);
}
Особенности:
navigation.state может принимать значения
idle, loading, submitting.Иногда необходимо управлять состоянием загрузки внутри конкретного
компонента, например, при динамическом запросе данных после
монтирования. В таких случаях используют useState и
useEffect:
import { useState, useEffect } from "react";
function Profile() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/profile")
.then(res => res.json())
.then(user => {
setData(user);
setLoading(false);
});
}, []);
if (loading) return <div>Загрузка профиля...</div>;
return <div>Имя пользователя: {data.name}</div>;
}
Особенности подхода:
Для улучшения UX можно использовать линейные
прогресс-бары, которые показывают постепенную загрузку данных
или переход между страницами. Например, библиотека
nprogress легко интегрируется с React Router:
import NProgress from "nprogress";
import { useNavigation } from "react-router-dom";
import { useEffect } from "react";
function NavigationProgress() {
const navigation = useNavigation();
useEffect(() => {
if (navigation.state === "loading") NProgress.start();
else NProgress.done();
}, [navigation.state]);
return null;
}
Вставка компонента NavigationProgress в корневой layout
позволяет отслеживать все переходы и асинхронные загрузки, обеспечивая
плавный пользовательский опыт.
loader
и navigation.state в React Router v6.4+ для точной
реакции на асинхронные действия.Эти подходы позволяют создать плавное и отзывчивое приложение, где пользователь всегда видит актуальный статус загрузки контента.