React Router
React Router - это библиотека для реализации маршрутизации на стороне клиента в
React-приложениях
.
Обратите внимание: данное руководство посвящено React Router
5 версии, в 6 версии API
рассматриваемой библиотеки претерпело несколько значительных изменений.
Примеры
Основная маршрутизация
В приведенном ниже примере у нас имеется 3 страницы, обрабатываемые роутером: главная (home), контакты (about) и страница с пользователями (users). При клике по <Link>
(ссылке) роутер рендерит соответствующий <Route>
(маршрут, путь).
Обратите внимание: за сценой <Link>
рендерит <a>
с настоящим href
, так что люди, использующие клавиатуру для навигации или экранные считываюющие устройства (screen readers), смогут без проблем пользоваться приложением.
import React from 'react'
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from 'react-router-dom'
export const App = () => (
<Router>
<header>
<nav>
<ul>
<li>
<Link to="/">Главная</Link>
</li>
<li>
<Link to="/about">Контакты</Link>
</li>
<li>
<Link to="/users">Пользователи</Link>
</li>
</ul>
</nav>
</header>
<main>
{/* <Switch> рендерит первый <Route>, совпавший с URL */}
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</main>
</Router>
)
const Home = () => <h2>Главная</h2>
const About = () => <h2>Контакты</h2>
const Users = () => <h2>Пользователи</h2>
Вложенный роутинг
Ниже приводится пример вложенного роутинга. Маршрут /topics
загружает компонент Topics
, который, в свою очередь, рендерит дальнейшие <Route>
на основе значения :id
.
import React from "react"
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useRouteMatch,
useParams
} from "react-router-dom"
export const App = () => (
<Router>
<header>
<nav>
<ul>
<li>
<Link to="/">Главная</Link>
</li>
<li>
<Link to="/about">Контакты</Link>
</li>
<li>
<Link to="/topics">Темы</Link>
</li>
</ul>
</nav>
</header>
<main>
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/topics">
<Topics />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</main>
</Router>
)
const Home = () => <h2>Главная</h2>
const About = () => <h2>Контакты</h2>
function Topics() {
const match = useRouteMatch()
return (
<>
<h2>Темы</h2>
<nav>
<ul>
<li>
<Link to={`${match.url}/components`}>Компоненты</Link>
</li>
<li>
<Link to={`${match.url}/props-vs-state`}>Пропы против состояния</Link>
</li>
</ul>
</nav>
{/* Страница Topics имеет собственный <Switch> с маршрутами,
основанными на URL /topics. Вы можете думать о втором
<Route> как о странице для остальных тем
или как о странице, отображаемой,
когда ни одна из тем не выбрана */}
<div>
<Switch>
<Route path={`${match.path}/:topicId`}>
<Topic />
</Route>
<Route path={match.path}>
<h3>Пожалуйста, выберите тему.</h3>
</Route>
</Switch>
</div>
</>
)
}
function Topic() {
const { topicId } = useParams()
return <h3>Идентификатор выбранной темы: {topicId}</h3>
}
Основные компоненты
В React Router
существует 3 категории компонентов:
- роутеры (routers), например,
<BrowserRouter>
или<HashRouter>
- маршруты (route matchers), например,
<Route>
или<Switch>
- и навигация (navigation), например,
<Link>
,<NavLink>
или<Redirect>
Все компоненты, используемые в веб-приложении, должны быть импортированы из react-router-dom
.
Роутеры
Любая маршрутизация начинается с роутера. Для веб-проектов react-router-dom
предоставляет <BrowserRouter>
и <HashRouter>
. Основное отличие между ними состоит в способе хранения URL и взаимодействия с сервером.
<BrowserRouter>
использует обычные URL. В этом случае URL выглядят как обычно, но требуется определенная настройка сервера. В частности, сервер должен обслуживать все страницы, используемые на клиенте.Create React App
поддерживает это из коробки в режиме разработки и содержит инструкции для правильной настройки сервера.<HashRouter>
хранить текущую локацию в хэш-части URL (после символа "#"), поэтому URL выглядит примерно так:http://example.com/#/your/page
. Поскольку хэш не отправляется серверу, его специальная настройка не требуется.
Для использования роутера необходимо обернуть в него компонент верхнего уровня:
import React from "react"
import ReactDOM from "react-dom"
import { BrowserRouter } from "react-router-dom"
const App = () => <h1>Привет, React Router</h1>
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
)
Маршруты
Существует 2 вида компонентов для поиска совпадений с URL: Switch
и Route
. При рендеринге <Switch>
определяет <Route>
, соответствующий текущему URL. При обнаружении такого маршрута, он рендерится, остальные маршруты игнорируются. Это означает, что вы должны помещать более специфические маршруты перед менее специфическими.
Если совпадения не найдено, <Switch>
ничего не рендерит (точнее, рендерит null
).
import React from "react"
import ReactDOM from "react-dom"
import {
BrowserRouter as Router,
Switch,
Route
} from "react-router-dom"
const App = () => (
<main>
<Switch>
{/* Если текущим URL является /about, рандерится данный маршрут,
остальные игнорируются */}
<Route path="/about">
<About />
</Route>
{/* Обратите внимание на порядок расположения этих двух маршрутов.
Более специфический path="/contact/:id" находится перед path="/contact" */}
<Route path="/contact/:id">
<Contact />
</Route>
<Route path="/contact">
<AllContacts />
</Route>
{/* Если ни один из предыдущих роутеров не совпал,
рендерится данный маршрут (он является резервным).
Важно: маршрут с path="/" всегда будет совпадать с
URL, поскольку все URL начинаются с /. Поэтому
мы поместили его последним */}
<Route path="/">
<Home />
</Route>
</Switch>
</main>
)
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
)
Обратите внимание, что <Route path>
ищет совпадение с началом, а не со всем URL. Поэтому <Route path="/">
всегда будет совпадать с URL. Поэтому в <Switch>
мы, обычно, помещаем его последним. Другим возможным решением является использование атрибута exact
: <Route exact path="/">
, который заставляет роутер искать полное совпадение.
Навигация
React Router
предоставляет компонент <Link>
для создания ссылок в приложении. При использовании <Link>
в HTML рендерится <a>
.
<Link to="/">Главная</Link>
// <a href="/">Главная</a>
<NavLink>
- это специальный тип <Link>
, позволяющий определять стили для активного состояния ссылки.
<NavLink to="/react" activeClassName="hurray">
React
</NavLink>
// Когда URL является /react, рендерится это:
// <a href="/react" className="hurray">React</a>
// Когда URL является другим:
// <a href="/react">React</a>
Для принудительной навигации используется <Redirect>
. При рендеринге <Redirect>
выполняется перенапр авление.
<Redirect to="/login">
API
Хуки
React Router
предоставляет несколько хуков для доступа к состоянию роутера и навигации внутри компонентов.
Указанные хуки предоставляют доступ к следующим объектам:
useHistory
Данный хук предоставляет доступ к экземпляру истории (history instance), который может использоваться для навигации:
import { useHistory } from 'react-router-dom'
function HomeButton() {
const history = useHistory()
function handleClick() {
history.push('/home')
}
return (
<button type="button" onClick={handleClick}>
Вернуться на главную
</button>
)
}
history
Термины "история" (history) и "объект истории" (history object) являются ссылками на пакет history, одну из двух зависимостей React Router
(вторая - сам React
), представляющий собой несколько реализаций, позволяющих управлять историей сессии с помощью JavaScript
в разных окружениях.
Объекты истории, как правило, имеют следующие свойства и методы:
length
: number - количество вхождений в стеке истории (history stack)action
: string - текущая операция (PUSH
,REPLACE
илиPOP
)location
: object - текущая локация. Может иметь следующие свойства:pathname
: string - адрес URLsearch
: string - строка запроса URLhash
: string - хэш-фрагмент URLstate
: object - специфичное для локации состояние, переданное, например, с помощьюpush(path, state)
при помещении локации в стек. Доступно только в браузере и истории, хранящейся в памяти
push(path, [state])
: func - помещает новое вхождение в стек историиreplace(path, [state])
: func - заменяет текущее вхождение в стеке историиgo(n)
: func - перемещает указатель в стеке истории на n вхожденийgoBack()
: func - эквивалент go(-1)goForward()
: func - эквивалент go(1)block(prompt)
: func - запрещает переход на другую страницу
useLocation
Данный хук возвращает объект локации (location object), содержащий информацию о текущем URL. Вы можете думать о нем как о useState
, возвращающем новую локацию при изменении URL.
Это может быть полезным в ситуации, когда необходимо получить новое представление страницы с помощью инструмента веб-аналитики при загрузке новой страницы, как в следующем примере:
import React from "react"
import ReactDOM from "react-dom"
import {
BrowserRouter as Router,
Switch,
useLocation
} from "react-router-dom"
function usePageViews() {
let location = useLocation()
React.useEffect(() => {
ga.send(["pageview", location.pathname])
}, [location])
}
function App() {
usePageViews()
return <Switch>...</Switch>
}
ReactDOM.render(
<Router>
<App />
</Router>,
node
)
location
Объект локации выглядит следующим образом:
{
key: 'ac3df4', // отсутствует в HashHistory
pathname: '/somewhere',
search: '?some=search-string',
hash: '#howdy',
state: {
[userDefined]: true
}
}
Объект локации доступен в:
- Компоненте
Route
какthis.props.location
(props.location
) - Пропе
render
компонентаRoute
как({ location }) => ()
- Потомках (
children
) компонентаRoute
как({ location }) => ()
withRouter
какthis.props.location
Он также доступен в history.location
.
Объект локации, в отличие от объекта истории, никогда не мутирует, поэтому его можно использовать для определения того, выполнялась ли навигация, например, для получения данных и запуска анимации.
Вы можете использовать location
для навигации вместо строк в:
Link to
для вебаLink to
для React NativeRedirect to
history.push
history.replace
Обычно, во всех перечисленных случаях мы используем строку, однако, когда необходимо добавить некоторое "состояние локации", которое будет доступным при возвращении приложения в данную локацию, можно использовать объект локации. Это может пригодиться в случае, когда определенные части интерфейса зависят от истории навигации, а не от путей (например, модульные окна).
// обычно, это все, что нам нужно
<Link to="/somewhere"/>
// однако, вместо этого можно использовать location
const location = {
pathname: '/somewhere',
state: { fromDashboard: true }
}
<Link to={location}/>
<Redirect to={location}/>
history.push(location)
history.replace(location)
Наконец, location
можно передавать в следующие компоненты:
Route
Switch
Это предотвратит использование ими актуальной локации из состоянии роутера. Это может быть полезным для анимации и отложенной навигации, а также для замены локации, в которой рендерится компонент.
useParams
Данный хук возвращает объект с параметрами URL в формате ключ: значение
. Используется для доступа к match.params
текущего <Route>
:
import React from "react"
import ReactDOM from "react-dom"
import {
BrowserRouter as Router,
Switch,
Route,
useParams
} from "react-router-dom"
function BlogPost() {
const { slug } = useParams()
return <div>Отображается пост {slug}</div>
}
ReactDOM.render(
<Router>
<Switch>
<Route exact path="/">
<HomePage />
</Route>
<Route path="/blog/:slug">
<BlogPost />
</Route>
</Switch>
</Router>,
node
)
useRouteMatch
Данный хук пытается найти совпадение для текущего URL подобно <Route>
. Он может быть полезен для доступа к совпадающим данным без рендеринга <Route>
.
Это позволяет вместо
import { Route } from "react-router-dom"
function BlogPost() {
return (
<Route
path="/blog/:slug"
render={({ match }) => {
// работаем с match...
return <div />
}}
/>
)
}
делать так
import { useRouteMatch } from "react-router-dom"
function BlogPost() {
const match = useRouteMatch("/blog/:slug")
// работаем с match...
return <div />
}
useRouteMatch
:
- без аргументов: возвращает объект совпадения текущего
<Route>
- принимает один аргумент, эквивалентный пропу
matchPath.argument
. Это может бытьpathname
в виде строки, как в приведенном выше примере, или объект с пропами, принимаемыми<Route>
, как в следующем примере:
const match = useRouteMatch({
path: "/BLOG/:slug/",
strict: true,
sensitive: true
})
match
Объект совпадения (match object) содержит информацию о том, насколько <Route path>
совпадает с URL. Он имеет следующие свойства:
params
: object - пары ключ/значение, соответствующие динамической части URLisExact
: bool -true
, если имеет место совпадениеpath
: string - паттерн пути, используемый для поиска совпадения. Используется для построения вложенных<Route>
url
: string - совпавшая часть URL. Используется для создания вложенных<Link>
Доступ к объекту совпадения можно получить в:
Route
какthis.props.match
(props.match
)- Пропе
render
компонентаRoute
как({ match }) => ()
- Потомках
Route
как({ match }) => ()
withRouter
какthis.props.match
matchPath
в виде возвращаемого значенияuseRouteMatch
в виде возвращаемого значения
Если Route
не имеет пропа path
и поэтому всегда совпадает, мы получаем его ближайшего предка. То же самое справделиво для withRouter
.