Маршруты: React Router
Много страниц в одном приложении
Настоящее приложение — это несколько страниц: главная, каталог, карточка товара, корзина, профиль.
Можно было бы хранить «текущую страницу» в состоянии и показывать нужный компонент через if. Но
тогда:
- адрес в браузере не меняется — страницу нельзя добавить в закладки или отправить другу;
- кнопки «Назад» и «Вперёд» не работают;
- после обновления страницы пользователь всегда оказывается на главной.
React-приложение — это одностраничное приложение (SPA): браузер загружает один HTML-файл, а дальше JavaScript сам перерисовывает страницу. Чтобы при этом работали адреса, нужна маршрутизация: адрес → какой компонент показать. Самая популярная библиотека для этого — React Router.
Как она работает: при клике по ссылке React Router не перезагружает страницу. Он меняет адрес
через History API браузера (history.pushState) и перерисовывает нужные компоненты. Получается
быстро, как в приложении, и при этом с настоящими адресами.
Подключение
В проекте библиотеку ставят командой npm install react-router-dom и импортируют нужное:
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
На нашем сайте она уже подключена как глобальный объект ReactRouterDOM:
const { BrowserRouter, Routes, Route, Link } = ReactRouterDOM;
Всё приложение оборачивают в роутер — обычно в main.jsx:
ReactDOM.createRoot(document.getElementById("root")).render(
<BrowserRouter>
<App />
</BrowserRouter>,
);
BrowserRouter работает с настоящим адресом браузера. В задачах на сайте код выполняется в
песочнице, где адресной строки нет, поэтому там используется MemoryRouter — он хранит адрес в
памяти. Всё остальное работает одинаково.
Routes и Route
function App() {
return (
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/catalog" element={<CatalogPage />} />
<Route path="/cart" element={<CartPage />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
}
<Routes>смотрит на текущий адрес и выбирает один подходящий маршрут.<Route path="…" element={…}>— «если адрес такой, показать этот элемент».path="*"совпадает с любым адресом — это страница 404 для всего, что не подошло.
Всё, что стоит вне <Routes> (шапка, меню, подвал), показывается на всех страницах.
Ссылки: Link и NavLink
Обычный <a href="/cart"> перезагрузит всю страницу и сбросит состояние приложения. Внутри
приложения используют Link:
<nav>
<Link to="/">Главная</Link>
<Link to="/catalog">Каталог</Link>
<NavLink to="/cart" className={({ isActive }) => (isActive ? "active" : "")}>
Корзина
</NavLink>
</nav>
Link рисует обычный <a>, но перехватывает клик. NavLink — то же самое, плюс знает, активна
ли ссылка: удобно подсвечивать текущий пункт меню.
Параметры адреса: useParams
У карточек товаров адреса вида /products/1, /products/2… Не писать же маршрут на каждый товар.
Изменяемую часть адреса обозначают двоеточием:
<Route path="/products/:id" element={<ProductPage />} />
const { useParams } = ReactRouterDOM;
function ProductPage() {
const { id } = useParams(); // для /products/42 → "42"
const product = products.find((p) => p.id === Number(id));
if (!product) return <p>Товар не найден</p>;
return <h1>{product.title}</h1>;
}
Параметры всегда строки: "42", а не 42. Сравнивая с числовым id, преобразуй через
Number. Обычно по параметру загружают данные: useFetch(/api/products/${id}) из прошлой темы.
Параметры после ? (/catalog?sort=price&page=2) читают через хук useSearchParams:
const [searchParams, setSearchParams] = useSearchParams();
const sort = searchParams.get("sort"); // "price"
setSearchParams({ sort: "name" }); // адрес станет ?sort=name
Переход из кода: useNavigate
Иногда переходить нужно не по клику на ссылку, а после действия: отправили форму — открыть страницу заказа, вышли из аккаунта — на главную:
const { useNavigate } = ReactRouterDOM;
function OrderForm() {
const navigate = useNavigate();
async function handleSubmit(e) {
e.preventDefault();
const order = await sendOrder();
navigate(`/orders/${order.id}`);
}
return <form onSubmit={handleSubmit}>…</form>;
}
navigate("/path")— перейти (как клик по ссылке, с записью в историю);navigate("/path", { replace: true })— заменить текущую запись: «Назад» не вернёт сюда;navigate(-1)— назад, как кнопка браузера.
Для перехода прямо во время отрисовки (например, «нет доступа — на страницу входа») есть компонент
<Navigate to="/login" replace />.
Вложенные маршруты и Outlet
У большинства страниц общий макет: шапка, меню, подвал. Его описывают родительским маршрутом,
а место для дочерних страниц обозначают <Outlet />:
const { Outlet } = ReactRouterDOM;
function Layout() {
return (
<div>
<header>
<Link to="/">Магазин</Link> <Link to="/cart">Корзина</Link>
</header>
<main>
<Outlet /> {/* сюда подставится текущая страница */}
</main>
<footer>© 2026</footer>
</div>
);
}
function App() {
return (
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<HomePage />} />
<Route path="/cart" element={<CartPage />} />
<Route path="/products/:id" element={<ProductPage />} />
</Route>
<Route path="/login" element={<LoginPage />} /> {/* без общего макета */}
</Routes>
);
}
Вложенность маршрутов повторяет вложенность интерфейса: Layout остаётся на месте (и сохраняет
своё состояние) при переходах между страницами внутри него.
Защищённые страницы
Страницу профиля или заказов нельзя показывать без входа. Частый приём — проверить и перенаправить:
function ProfilePage() {
const { user } = useAuth(); // свой хук над контекстом из темы про useContext
if (!user) return <Navigate to="/login" replace />;
return <h1>Привет, {user.name}</h1>;
}
Важно понимать: это защита интерфейса, а не данных. Настоящую проверку доступа всегда делает сервер — код в браузере пользователь может изменить.
Деплой SPA
Когда приложение выложено на хостинг, адрес /products/42 для сервера — путь к несуществующему
файлу. Поэтому хостинг настраивают так, чтобы на любой адрес отдавался index.html, а дальше
маршрут выберет React Router. На Netlify, Vercel и Cloudflare Pages для этого есть простая настройка
(или она включена по умолчанию для SPA).
Итог
- SPA: одна HTML-страница, переходы без перезагрузки; React Router связывает адрес с компонентами.
<BrowserRouter>вокруг приложения;<Routes>+<Route path element>;path="*"— 404.<Link to>вместо<a href>;NavLinkзнает, активна ли ссылка./products/:id+useParams()— параметры адреса (строки!);useSearchParams— после?.useNavigate()— переход из кода;<Navigate>— перенаправление при отрисовке.- Общий макет — родительский
Routeс<Outlet />.