Если вы здесь, значит, вы уже успели познакомиться с компонентами, состоянием, хуками и даже с управлением глобальным состоянием. Это огромный пласт работы и вы большие молодцы! Но до сих пор все наши приложения жили на одной-единственной странице. Сегодня мы кардинально меняем этот подход.
В этом уроке мы с вами выйдем на новый уровень и научимся создавать настоящие многостраничные приложения. Мы будем использовать инструмент React Router. Это не официальная библиотека от создателей React, но она является де-факто стандартом для маршрутизации в React-приложениях. Представьте себе любой современный веб-сайт. У него есть главная страница, страница «О нас», «Контакты», «Блог». При переходе по этим разделам страница не перезагружается полностью, а плавно обновляет лишь часть контента. Это и есть SPA (Single Page Application) с клиентской маршрутизацией и сегодня мы научимся это делать.
Без маршрутизации наше приложение было бы монолитным, неудобным для пользователя и плохо индексируемым поисковыми системами. Маршрутизация позволяет нам организовать код логически, разделить функционал по разным URL-адресам и создать полноценный пользовательский опыт. Мы будем использовать на момент написания стабильную версию React Router v6. Она принесла с собой более простой и интуитивно понятный синтаксис, что сделает нашу работу еще приятнее.
Что такое React Router?
React Router это библиотека, которая позволяет нам «отображать» разные компоненты в зависимости от пути (URL) в адресной строке браузера. Важно понимать фундаментальный принцип: в SPA у нас по-прежнему всего один HTML-файл (обычно index.html). React Router не загружает новые HTML-страницы с сервера. Вместо этого он динамически, на стороне клиента, меняет отображаемые React-компоненты, создавая иллюзию перехода между страницами. При этом URL в браузере меняется и мы можем использовать кнопки «Назад» и «Вперед» для навигации по истории.
Давайте разберемся на простой аналогии. Представьте, что ваше приложение это театр. Сцена это область, где происходит основное действие. React Router это режиссер, который решает, какая пьеса (какой компонент) будет играться на сцене в зависимости от афиши (URL). Когда афиша меняется (пользователь вводит новый URL или нажимает на ссылку), режиссер быстро меняет декорации и актеров на сцене, не перестраивая весь театр заново. Это и есть суть клиентской маршрутизации.
Без React Router нам пришлось бы управлять видимостью компонентов с помощью многочисленных условных операторов (например, isHomePage ? <Home /> : isAboutPage ? <About /> : null), что очень быстро превратилось бы в нечитаемый и неуправляемый код. React Router предоставляет нам декларативный способ описания структуры нашего приложения, что делает код чище и предсказуемее.
Установка и начальная настройка
Первым делом нам нужно установить библиотеку в наш проект. Если вы создавали проект с помощью create-react-app, просто откройте терминал в корневой папке проекта и выполните команду:
npm install react-router-dom
Эта команда скачает и установит последнюю версию react-router-dom (DOM-версия для веб-приложений) в папку node_modules и добавит запись в ваш package.json.
Теперь давайте настроим базовую структуру. Основной «мозговой центр» маршрутизации находится в компоненте BrowserRouter. Обычно его принято оборачивать вокруг корневого компонента приложения, то есть вокруг App. Давайте откроем наш index.js и внесем изменения.
До:
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> );
После:
import React from 'react'; import ReactDOM from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; // Импортируем BrowserRouter import './index.css'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <BrowserRouter> {/* Оборачиваем App в BrowserRouter */} <App /> </BrowserRouter> </React.StrictMode> );
Вот и все! Теперь все наше приложение имеет доступ к функционалу маршрутизации. Компонент BrowserRouter использует HTML5 History API для отслеживания истории сессии и синхронизации UI с URL.
Создаем основные страницы-компоненты
Прежде чем настраивать маршруты, давайте создадим несколько простых компонентов, которые будут выступать в роли наших «страниц». Обычно их принято размещать в папке src/pages или src/views, чтобы отделить от более мелких, переиспользуемых компонентов.
Создадим папку src/pages и внутри нее три файла:
Home.js
function Home() { return ( <div> <h1>Добро пожаловать на наш сайт!</h1> <p>Это главная страница. Здесь может быть ваш баннер, последние новости или что-то еще важное.</p> </div> ); } export default Home;
About.js
function About() { return ( <div> <h1>О нас</h1> <p>Мы это маленькая, но гордая команда разработчиков, которая обожает React и хочет научить ему всех.</p> </div> ); } export default About;
Contact.js
function Contact() { return ( <div> <h1>Свяжитесь с нами</h1> <p>Напишите нам на email: hello@example.com</p> </div> ); } export default Contact;
Пока что это самые обычные функциональные компоненты. Ничего магического в них нет. Они станут «страницами» благодаря роутеру.
Определяем маршруты с помощью Routes и Route
Теперь самое интересное, свяжем URL с нашими компонентами. В React Router v6 для этого используются компоненты Routes и Route. Мы идем в наш главный компонент App.js и полностью его переделываем.
App.js
import { Routes, Route } from 'react-router-dom'; import Home from './pages/Home'; import About from './pages/About'; import Contact from './pages/Contact'; import './App.css'; function App() { return ( <div className="App"> {/* Область для навигации, которую мы создадим позже */} {/* Основная контентная область */} <main> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/contact" element={<Contact />} /> </Routes> </main> </div> ); } export default App;
Давайте разберем, что здесь происходит:
-
<Routes>. Это контейнер для всех отдельных правил маршрутизации (<Route>). Он проверяет все дочерниеRouteи отрисовывает первый, чейpathсовпадает с текущим URL. Он пришел на смену компонентуSwitchиз v5. -
<Route>. Это индивидуальное правило. У него есть два ключевых атрибута:-
path. Строка, которая описывает путь в URL (например,/about). -
element. React-компонент, который должен быть отрендерен, когда путь совпадает.
-
Обратите внимание на маршрут path="/". Это корневой путь, то есть главная страница нашего сайта. Теперь, если вы запустите приложение (npm start), вы увидите главную страницу. Попробуйте вручную ввести в адресной строке http://localhost:3000/about и вы увидите компонент «О нас»! Это уже работает.
Навигация с помощью компонента Link
Конечно, вводить пути вручную это не пользовательский опыт. Нам нужна навигационная панель с ссылками. В обычном HTML мы бы использовали тег <a href="/about">О нас</a>. Но в React SPA это плохая идея! Почему? Потому что такой тег заставит браузер сделать полноценный запрос на сервер и перезагрузить всю страницу, что уничтожит все состояние нашего React-приложения и свежет на нет все преимущества SPA.
Вместо этого React Router предоставляет нам компонент <Link>.
Давайте создадим компонент Navigation.js:
components/Navigation.js
import { Link } from 'react-router-dom'; function Navigation() { return ( <nav> <ul style={{ display: 'flex', listStyle: 'none', gap: '20px' }}> <li> <Link to="/">Главная</Link> </li> <li> <Link to="/about">О нас</Link> </li> <li> <Link to="/contact">Контакты</Link> </li> </ul> </nav> ); } export default Navigation;
Ключевой момент здесь, это атрибут to. Он аналогичен href, но при клике на такую ссылку React Router перехватывает событие, предотвращает перезагрузку страницы и динамически обновляет UI и URL.
Теперь импортируем и добавим Navigation в наш App.js:
App.js (обновленный)
import { Routes, Route } from 'react-router-dom'; import Home from './pages/Home'; import About from './pages/About'; import Contact from './pages/Contact'; import Navigation from './components/Navigation'; // Импортируем навигацию import './App.css'; function App() { return ( <div className="App"> <Navigation /> {/* Добавляем навигацию */} <main> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/contact" element={<Contact />} /> </Routes> </main> </div> ); } export default App;
Поздравляю! Теперь у вас есть работающее многостраничное приложение. Нажимайте на ссылки и наблюдайте, как контент меняется мгновенно, без перезагрузки страницы.
Динамические маршруты и параметры URL
Часто нам нужно отображать страницы на основе какого-то идентификатора. Например, страница конкретного товара, профиль пользователя или пост в блоге. Для этого используются динамические сегменты в пути.
Давайте создадим страницу для блога. Предположим, у нас есть список постов и мы хотим, чтобы каждый пост открывался по адресу /posts/1, /posts/2 и т.д.
Сначала создадим компонент Blog.js, который будет выводить список постов (пока заглушки).
pages/Blog.js
import { Link } from 'react-router-dom'; const POSTS = [ { id: '1', title: 'Пост 1' }, { id: '2', title: 'Пост 2' }, { id: '3', title: 'Пост 3' }, ]; function Blog() { return ( <div> <h1>Блог</h1> <ul> {POSTS.map(post => ( <li key={post.id}> {/* Ссылка ведет на динамический маршрут */} <Link to={`/posts/${post.id}`}>{post.title}</Link> </li> ))} </ul> </div> ); } export default Blog;
Теперь создадим компонент Post.js, который будет отображать один пост. Нам как-то нужно получить id поста из URL. Для этого в пути маршрута мы указываем параметр, начав его с двоеточия.
Добавим маршруты в App.js:
App.js (добавляем маршруты для блога)
// ... остальные импорты import Blog from './pages/Blog'; import Post from './pages/Post'; function App() { return ( <div className="App"> <Navigation /> <main> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/contact" element={<Contact />} /> <Route path="/blog" element={<Blog />} /> {/* Новый маршрут для списка постов */} <Route path="/posts/:postId" element={<Post />} /> {/* Динамический маршрут! */} </Routes> </main> </div> ); }
Обратите внимание на path="/posts/:postId". Часть :postId это параметр. Теперь внутри компонента Post мы можем получить его значение с помощью хука useParams.
pages/Post.js
import { useParams } from 'react-router-dom'; function Post() { // Хук useParams возвращает объект с параметрами нашего маршрута. const { postId } = useParams(); // В реальном приложении здесь мы бы делали запрос к API, передавая postId. // Но пока просто выведем его. return ( <div> <h1>Страница поста</h1> <p>Вы просматриваете пост с <strong>ID: {postId}</strong></p> </div> ); } export default Post;
Теперь, если вы перейдете по ссылке «Пост 1» на странице /blog, вы попадете на /posts/1 и компонент Post отобразит «ID: 1». Это и есть основа для создания страниц с динамическим контентом!
Программная навигация с помощью useNavigate
Бывают ситуации, когда нам нужно перейти на другую страницу не по клику на ссылку, а в результате какого-то действия: после отправки формы, успешной авторизации и т.д. Для этого используется хук useNavigate.
Давайте смоделируем этот сценарий. Создадим простую страницу входа (Login.js). При нажатии на кнопку «Войти» мы «выполним вход» и программно перенаправим пользователя на главную страницу.
pages/Login.js
import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; function Login() { const [username, setUsername] = useState(''); const navigate = useNavigate(); // Хук useNavigate возвращает функцию const handleSubmit = (e) => { e.preventDefault(); // Здесь обычно идет запрос к API для проверки логина/пароля console.log(`Пользователь ${username} выполнил вход`); // После успешного входа перенаправляем пользователя navigate('/'); // Переход на главную страницу // Можно также использовать navigate(-1) для шага назад, как history.goBack() }; return ( <div> <h1>Страница входа</h1> <form onSubmit={handleSubmit}> <input type="text" placeholder="Введите имя" value={username} onChange={(e) => setUsername(e.target.value)} /> <button type="submit">Войти</button> </form> </div> ); } export default Login;
Не забудьте добавить маршрут для страницы входа в App.js! Функция navigate это очень мощный инструмент, который позволяет гибко управлять навигацией в вашем приложении.
Обработка несуществующих маршрутов (404)
Что произойдет, если пользователь введет несуществующий URL, например, /some-non-existent-page? Наше приложение просто покажет пустую странию, потому что ни один Route не совпадет с этим путем. Это плохой пользовательский опыт. Давайте это исправим, создав компонент для страницы 404.
Создадим NotFound.js:
pages/NotFound.js
import { Link } from 'react-router-dom'; function NotFound() { return ( <div> <h1>Ой! 404</h1> <p>Запрошенная страница не существует.</p> <Link to="/">Вернуться на главную</Link> </div> ); } export default NotFound;
Теперь нужно сообщить роутеру, что этот компонент должен рендериться по умолчанию, если ни один другой маршрут не совпал. Для этого мы используем path="*".
App.js (добавляем маршрут 404)
// ... остальные импорты import NotFound from './pages/NotFound'; function App() { return ( <div className="App"> <Navigation /> <main> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/contact" element={<Contact />} /> <Route path="/blog" element={<Blog />} /> <Route path="/posts/:postId" element={<Post />} /> <Route path="/login" element={<Login />} /> {/* Этот маршрут сработает для любого пути, не указанного выше */} <Route path="*" element={<NotFound />} /> </Routes> </main> </div> ); }
Теперь при переходе на любой неизвестный URL пользователь увидит нашу кастомную страницу 404 с ссылкой для возврата. Это гораздо лучше!
Практические задачи для закрепления
Давайте закрепим пройденный материал, выполнив несколько задач. Постарайтесь сделать их самостоятельно, прежде чем смотреть на подсказки.
Задача 1: Создайте страницу «Услуги»
-
Создайте новый компонент
Services.jsв папкеpages. -
Добавьте для него маршрут
/services. -
Добавьте ссылку на эту страницу в компонент
Navigation.
Задача 2: Сделайте «хлебные крошки» или активную ссылку
-
Используя хук
useLocation, подсветите текущую активную ссылку в навигации. Например, добавьте классactiveк тому пункту меню, на странице которого находится пользователь.-
Подсказка.
const location = useLocation();.location.pathnameсодержит текущий путь.
-
Задача 3: Создайте страницу профиля пользователя с вложенными маршрутами
-
Создайте маршрут
/profile. -
В компоненте
Profileсоздайте свою мини-навигацию с двумя ссылками:/profile/postsи/profile/settings. -
Используйте компонент
RoutesиRouteвнутри компонентаProfile, чтобы отображать разные разделы (ProfilePosts,ProfileSettings) в зависимости от пути.-
Подсказка: Это называется «вложенные маршруты (Nested Routes)». Для относительных пучей используйте просто
postsвместо/profile/posts.
-
Пример кода для задачи 3:
App.js (добавляем профиль)
// ... import Profile from './pages/Profile'; function App() { return ( // ... <Routes> {/* ... другие маршруты ... */} <Route path="/profile/*" element={<Profile />} /> {/* Обратите внимание на звездочку! Она важна для вложенности */} </Routes> // ... ); }
pages/Profile.js
import { Link, Routes, Route, useLocation } from 'react-router-dom'; import ProfilePosts from './ProfilePosts'; import ProfileSettings from './ProfileSettings'; function Profile() { const location = useLocation(); return ( <div> <h1>Ваш профиль</h1> <nav> <Link to="posts" style={{ fontWeight: location.pathname.includes('/posts') ? 'bold' : 'normal' }}>Мои посты</Link> {' | '} <Link to="settings" style={{ fontWeight: location.pathname.includes('/settings') ? 'bold' : 'normal' }}>Настройки</Link> </nav> {/* Вложенные маршруты рендерятся здесь */} <div style={{ marginTop: '20px' }}> <Routes> <Route path="posts" element={<ProfilePosts />} /> <Route path="settings" element={<ProfileSettings />} /> {/* Можно добавить индексный маршрут для /profile */} <Route path="/" element={<div>Выберите раздел профиля</div>} /> </Routes> </div> </div> ); } export default Profile;
pages/ProfilePosts.js
function ProfilePosts() { return <div>Здесь будут ваши посты...</div>; } export default ProfilePosts;
pages/ProfileSettings.js
function ProfileSettings() { return <div>Настройки профиля...</div>; } export default ProfileSettings;
Итоги и заключение
Вы только что прошли один из самых сложных уроков в освоении современной фронтенд-разработки на React.
Сегодня мы с вами:
-
Поняли принцип клиентской маршрутизации и чем она отличается от традиционной серверной.
-
Установили и настроили
react-router-domв нашем проекте. -
Создали свои первые «страницы» как обычные React-компоненты.
-
Настроили маршрутизацию с помощью
RoutesиRoute, связав пути URL с компонентами. -
Реализовали навигацию с помощью компонента
Link, не вызывающую перезагрузки страницы. -
Освоили динамические маршруты и хук
useParamsдля создания страниц поста, товара и т.д. -
Научились перенаправлять пользователя программно с помощью хука
useNavigate. -
Позаботились о пользователях, добавив обработку несуществующих страниц (404).
Теперь ваше приложение обрело структуру и стало по-настоящему навигационным. Это фундамент для создания любых сложных интерфейсов: от интернет-магазина до социальной сети.
Если вы хотите освоить React полностью, от основ до продвинутых тем, приглашаю вас посмотреть на полный курс с уроками по React для начинающих. Там вас ждут структурированные материалы, больше практических проектов и глубокие объяснения.
Удачи в изучении и до следующего урока!
Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.


