window.history як фундамент маршрутизації в Single Page Applications.BrowserRouter, компонент Routes та механізм співпадіння шляхів (Route Matching).useParams) та Query-рядки (useSearchParams).<Outlet />.useNavigate, переадресація (<Navigate />) та управління стеком історії браузера.Концепція клієнтської маршрутизації. Налаштування BrowserRouter, Routes та Route. Вивчення динамічних параметрів шляху (useParams), вкладених маршрутів (nested routes) та програмної навігації (useNavigate).
В епоху класичних веб-сайтів (MPA - Multi-Page Applications) навігація між сторінками означала надсилання нового HTTP-запиту до сервера, отримання нового HTML-документа і повне “блимання” екрану при його перемальовуванні браузером.
Сучасні React-додатки будуються за парадигмою SPA (Single Page Application). Сервер віддає index.html лише один раз (при першому заході користувача). Всі подальші “переходи” між сторінками — це ілюзія. JavaScript просто перехоплює клік по посиланню, змінює URL-адресу в стрічці браузера і підміняє один React-компонент на інший прямо в пам’яті. Бібліотека React Router є світовим стандартом (De Factor Standard) для керування цією ілюзією, перетворюючи набір розрізнених компонентів на повноцінний програмний продукт із власною архітектурою шляхів.
Щоб зрозуміти цінність React Router, порівняємо дві парадигми:
Server-Side Routing (Традиційний PHP, Python Django):
<a href="/about">.useState) втрачено назавжди.Client-Side Routing (React + React Router):
<Link to="/about">.e.preventDefault()). ЗАПИТ НА СЕРВЕР НЕ ЙДЕ!window.history.pushState().window.history як фундаментЖодна клієнтська бібліотека маршрутизації не працювала б без нативного HTML5 History API.
До появи HTML5 розробникам доводилося використовувати “хеш-маршрутизацію” (Hash Routing), де URL виглядав як site.com/#/about. Зміна всього, що йде після #, не викликає перезавантаження сторінки в браузері.
З появою History API браузери надали JavaScript-методи для реальної маніпуляції URL:
history.pushState({}, '', '/about') — змінити URL на /about і додати запис в історію браузера (щоб працювала кнопка “Назад”).window.addEventListener('popstate', callback) — браузер повідомляє ваш код, коли користувач натиснув апаратну кнопку “Назад” або “Вперед”.React Router інкапсулює цю логіку, надаючи декларативний інтерфейс для розробників.
Бібліотека React Router пережила кілька радикальних архітектурних змін. Для розробника 4-го курсу важливо знати, що версія v6 принесла зміну парадигми.
Принципові відмінності v6 від старих версій:
<Switch> на користь <Routes>.exact). У v6 алгоритм сам знаходить найбільш специфічний (точний) маршрут, незалежно від того, як ви відсортували компоненти в коді.Будь-який маршрутизатор потребує Провайдера, який відстежує стан URL.
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
const App = () => {
return (
// 1. Огортаємо РУТ додатка в провайдер. Він парсить поточний URL.
<BrowserRouter>
{/* 2. Навігація замість <a> тегів */}
<nav>
<Link to="/">Головна</Link>
<Link to="/about">Про нас</Link>
</nav>
{/* 3. Зона, де буде мінятися контент */}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
{/* Wildcard (Зірочка) для сторінки помилки (404 Not Found) */}
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
};
Важливо: Використання звичайного <a href="/about"> “ламає” SPA, оскільки браузер виконає жорстке перезавантаження. Компонент <Link> гарантує, що перехід відбудеться безшовно (без втрати стану useState).
Інженерним стандартом є створення сторінок, які можуть приймати параметри (наприклад, відображення профілю конкретного користувача: /users/123, /users/alex).
Визначаються двокрапкою : у path.
// 1. Визначення маршруту
<Route path="/users/:userId" element={<UserProfile />} />;
// 2. Отримання в компоненті через хук useParams
import { useParams } from "react-router-dom";
const UserProfile = () => {
// З URL "/users/123" хук витягне { userId: '123' }
const { userId } = useParams();
useEffect(() => {
fetchUserData(userId); // Фетчимо дані для поточного ID
}, [userId]);
return <div>Профіль користувача №{userId}</div>;
};
Це частина URL після знаку запитання, яка використовується для фільтрації, пагінації чи пошуку (напр., /products?category=laptops&sort=price).
Вони не визначаються в конфігурації <Route>, а просто зчитуються “на льоту”.
import { useSearchParams } from "react-router-dom";
const ProductList = () => {
// useSearchParams працює аналогічно до useState (гетер і функція сеттер)
const [searchParams, setSearchParams] = useSearchParams();
const category = searchParams.get("category"); // "laptops"
const handleSort = () => {
// Збереже поточні параметри і змінить/додасть новий
setSearchParams({ category, sort: "price" });
};
};
Перевага useSearchParams полягає в тому, що стан фільтрів чи пагінації зберігається безпосередньо в URL. Якщо користувач скопіює посилання і відправить другу, друг побачить точно той самий відфільтрований список. Це називається “Deep Linking”. Знаходження фільтрів у локальному useState не дало б такої можливості.
<Outlet />)В ентерпрайз-додатках сторінки часто мають спільний “каркас” (Layout) — наприклад, панель адміністратора з єдиним лівим меню та верхнім хедером.
У React Router v6 для цього використовується унікальний компонент вікна прокидання — <Outlet />.
// 1. Структура макету
const DashboardLayout = () => {
return (
<div className="layout">
<Sidebar />
<main className="content">
{/* Сюди "проваляться" вкладені (дочірні) сторінки */}
<Outlet />
</main>
</div>
);
};
// 2. Налаштування роутингу
<Routes>
{/* Батьківський маршрут. Має префікс /admin */}
<Route path="/admin" element={<DashboardLayout />}>
{/* Дочірні маршрути автоматично успадковують префікс /admin */}
<Route index element={<DashboardHome />} /> {/* URL: /admin */}
<Route path="users" element={<AdminUsers />} /> {/* URL: /admin/users */}
<Route path="settings" element={<AdminSettings />} />{" "}
{/* URL: /admin/settings */}
</Route>
</Routes>;
Якщо ви введете URL /admin/users, React Router відрендерить компонент <DashboardLayout>, а замість його внутрішнього <Outlet /> відрендерить компонент <AdminUsers>. Це радикально скорочує дублювання коду обгорток.
useNavigateІноді перехід між сторінками має відбуватися не в результаті кліку по посиланню (Link), а як наслідок певної дії бізнес-логіки (успішний логін, завершення оплати в кошику, таймаут сесії). Це називається Програмна (Імперативна) навігація.
import { useNavigate } from "react-router-dom";
const LoginForm = () => {
const navigate = useNavigate();
const handleLogin = async () => {
const isSuccess = await authenticateUser();
if (isSuccess) {
// Імперативний перехід на Dashboard
navigate("/dashboard", {
replace: true, // Опція replace видаляє сторінку логіну з історії браузера
});
}
};
return <button onClick={handleLogin}>Увійти</button>;
};
Опція { replace: true } (Replace State) гарантує, що якщо користувач перейде на Dashboard і натисне кнопку “Назад” у браузері, його не поверне назад на сторінку логіну (де він щойно успішно авторизувався), що є правильним UX-патерном для потоків автентифікації.
Крім хука useNavigate, існує декларативний компонент-аналог <Navigate />, який найчастіше використовується для написання логіки захищених маршрутів (Protected Routes):
const ProtectedRoute = ({ children }) => {
const { user } = useAuth();
if (!user) {
// Якщо користувач не залогінений - миттєво редиректимо його на /login
return <Navigate to="/login" replace />;
}
return children;
};
window.history), надаючи розробнику декларативний інтерфейс для побудови архітектури шляхів.<Routes>, зробивши використання exact-прапорців непотрібним.<Outlet> дозволяють легко і без дублювання створювати багаторівневі макети (Layouts), наприклад, розділяючи публічну частину додатку та захищену панель адміністратора.useParams - ідентифікація ресурсів) та Search Query (useSearchParams - налаштування переглядів, фільтрація) є необхідним для побудови правильної архітектури Deep-лінків у додатку.useNavigate з розумінням дії параметра replace дозволяє правильно управляти стеком (історією) візитів браузера, запобігаючи логічним пасткам (наприклад, повернення на форму оплати після її успішного завершення натисканням клавіші “Назад”).<a href="/about"> порівняно з компонентом <Link to="/about"> у React Router? Що станеться з об’єктом стану Redux/Zustand у кожному з двох випадків?<Switch>)? Чому порядок визначення маршрутів більше не має критичного значення?<Outlet />? Опишіть патерн “Вкладені маршрути” (Nested Routes) на прикладі панелі управління інтернет-магазину.useParams та useSearchParams. Який із цих хуків доцільно використати для сторінки детального опису товару, а який — для сортування цього товару за ціною?/login (в системі захищених рутів) інженери обов’язково додають опцію { replace: true } у метод navigate()? Як це впливає на апаратні кнопки браузера?