React.js. Маршрутизация. Часть вторая из трех
23.08.2021
Теги: Frontend • JavaScript • React.js • Web-разработка • Маршрутизация • Навигация • Теория
Дочерние маршруты
В рамках маршрутов в React можно определять дочерние маршруты. Такие подмаршруты будут отсчитываться от главного маршрута. Давайте добавим в наше приложение маленький каталог товаров, который будет содержать два раздела и четыре товара (по два в каждом разделе). Для этого создадим директорию src/catalog
и разместим в ней семь компонентов.
import React from 'react'; import {Link, Route, Switch} from 'react-router-dom'; import Phones from './Phones'; import Tablets from './Tablets'; export default function Catalog() { return ( <React.Fragment> <h1>Catalog</h1> <ul> <li><Link to="/catalog/phones">Phones</Link></li> <li><Link to="/catalog/tablets">Tablets</Link></li> </ul> <Switch> <Route path="/catalog/phones" component={Phones} /> <Route path="/catalog/tablets" component={Tablets} /> </Switch> </React.Fragment> ); }
import React from 'react'; import {Link, Route, Switch} from 'react-router-dom'; import PhoneOne from './PhoneOne'; import PhoneTwo from './PhoneTwo'; export default function Phones() { return ( <React.Fragment> <h3>Phones</h3> <ul> <li><Link to="/catalog/phones/phone-one">Phone One</Link></li> <li><Link to="/catalog/phones/phone-two">Phone Two</Link></li> </ul> <Switch> <Route path="/catalog/phones/phone-one" component={PhoneOne} /> <Route path="/catalog/phones/phone-two" component={PhoneTwo} /> </Switch> </React.Fragment> ); }
import React from 'react'; import {Link, Route, Switch} from 'react-router-dom'; import TabletOne from './TabletOne'; import TabletTwo from './TabletTwo'; export default function Tablets() { return ( <React.Fragment> <h3>Tablets</h3> <ul> <li><Link to="/catalog/tablets/tablet-one">Tablet One</Link></li> <li><Link to="/catalog/tablets/tablet-two">Tablet Two</Link></li> </ul> <Switch> <Route path="/catalog/tablets/tablet-one" component={TabletOne} /> <Route path="/catalog/tablets/tablet-two" component={TabletTwo} /> </Switch> </React.Fragment> ); }
import React from 'react'; export default function PhoneOne() { return ( <React.Fragment> <h5>Phone One</h5> <p>Description phone one</p> </React.Fragment> ); }
import React from 'react'; export default function PhoneTwo() { return ( <React.Fragment> <h5>Phone Two</h5> <p>Description phone two</p> </React.Fragment> ); }
import React from 'react'; export default function TabletOne() { return ( <React.Fragment> <h5>Tablet One</h5> <p>Description tablet one</p> </React.Fragment> ); }
import React from 'react'; export default function TabletTwo() { return ( <React.Fragment> <h5>Tablet Two</h5> <p>Description tablet Two</p> </React.Fragment> ); }
Добавим ссылку на каталог в компонент NavBar
:
import {NavLink} from 'react-router-dom'; export default function NavBar() { return ( <div> <NavLink exact to="/" activeClassName="active">Home</NavLink> <NavLink to="/catalog" activeClassName="active">Catalog</NavLink> <NavLink to="/about" activeClassName="active">About</NavLink> <NavLink to="/contact" activeClassName="active">Contact</NavLink> </div> ) }
И добавим в компонент Content
еще один Route
:
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'; import Home from '../pages/Home'; import About from '../pages/About'; import Contact from '../pages/Contact'; import NotFound from '../pages/NotFound'; import NavBar from './NavBar'; import Catalog from '../catalog/Catalog'; export default function Content() { return ( <main className="container"> <Router> <NavBar /> <Switch> <Route exact path="/" component={Home} /> <Route path="/catalog" component={Catalog} /> <Route exact strict path="/about" component={About} /> <Route exact strict path="/contact" component={Contact} /> <Route component={NotFound} /> </Switch> </Router> </main> ); }
Разберем, как это работает для ссылки /catalog
. Сначала отработает Switch
внутри Content
, будет совпадение на втором маршруте — вызывается компонент Catalog
. Потом отработает Switch
внутри Catalog
— совпадений не найдено, поэтому в браузере мы увидим только заголовок <h1>
и ссылки на разделы Phones
и Tablets
.
<h1>Catalog</h1> <ul> <li><a href="/catalog/phones">Phones</a></li> <li><a href="/catalog/tablets">Tablets</a></li> </ul>
Теперь посмотрим, что будет для ссылки /catalog/phones
. Сначала отработает Switch
внутри Content
, будет совпадение на втором маршруте — вызывается компонент Catalog
. Потом отработает Switch
внутри Catalog
, будет совпадение на первом маршруте — вызывается компонент Phones
, поэтому в браузере мы увидим только заголовок <h1>
и ссылки на разделы Phones
и Tablets
, заголовок <h3>
и ссылки на товары PhoneOne
и PhoneTwo
.
<h1>Catalog</h1> <ul> <li><a href="/catalog/phones">Phones</a></li> <li><a href="/catalog/tablets">Tablets</a></li> </ul> <h3>Phones</h3> <ul> <li><a href="/catalog/phones/phone-one">Phone One</a></li> <li><a href="/catalog/phones/phone-two">Phone Two</a></li> </ul>
Теперь посмотрим, что будет для ссылки /catalog/phones/phone-one
. Сначала отработает Switch
внутри Content
, будет совпадение на втором маршруте — вызывается компонент Catalog
. Потом отработает Switch
внутри Catalog
, будет совпадение на первом маршруте — вызывается компонент Phones
. Потом отработает Switch
внутри Phones
, будет совпадение на первом маршруте — вызывается компонент PhoneOne
.
<h1>Catalog</h1> <ul> <li><a href="/catalog/phones">Phones</a></li> <li><a href="/catalog/tablets">Tablets</a></li> </ul> <h3>Phones</h3> <ul> <li><a href="/catalog/phones/phone-one">Phone One</a></li> <li><a href="/catalog/phones/phone-two">Phone Two</a></li> </ul> <h5>Phone One</h5> <p>Description phone one</p>
Переадресация
Компонент Redirect
определяет два атрибута — to
(новый путь) и from
(путь, с которого надо выполнить переадресацию).
<Router>
<div>
<nav>
<Link to="/">Home</Link>
<Link to="/old">Old page</Link>
<Link to="/new">New page</Link>
</nav>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/new" component={NewPage} />
<Redirect from="/old" to="/new" />
</Switch>
</div>
</Router>
Параметры маршрутов
Часто возникает необходимость использовать различные параметры в маршрутах для передачи другим страницам:
<Switch>
<Route path="/catalog" component={CatalogShow} />
<Route path="/catalog/category/:id" component={CategoryShow} />
<Route path="/catalog/product/:id" component={ProductShow} />
<Route path="/pages/page/:id" component={PageShow} />
</Switch>
В маршруты можно передавать неограниченное количество параметров:
<Switch>
<Route path="/catalog" component={CatalogShow} />
<Route path="/catalog/category/:id/:name" component={CategoryShow} />
<Route path="/catalog/product/:id/:name" component={ProductShow} />
<Route path="/pages/page/:id/:name" component={PageShow} />
</Switch>
Можно отметить часть параметров как необязательные:
<Switch>
<Route path="/catalog" component={CatalogShow} />
<Route path="/catalog/category/:id/:name?" component={CategoryShow} />
<Route path="/catalog/product/:id/:name?" component={ProductShow} />
<Route path="/pages/page/:id/:name?" component={PageShow} />
</Switch>
В примере выше компоненты ожидают параметр id
. И для этого параметра можно предать любое значение — строку, число и т.д. Но допустим, нам нужно, чтобы для параметра можно было передать только числовое значение. В этом случае нужно использовать в качестве ограничения маршрута регулярные выражения.
<Switch>
<Route path="/catalog" component={CatalogShow} />
<Route path="/catalog/category/:id(\d+)/:name?" component={CategoryShow} />
<Route path="/catalog/product/:id(\d{5}-\d{3}-\d{4})/:name?" component={ProductShow} />
<Route path="/pages/page/:id(\d+)/:name?" component={PageShow} />
</Switch>
Если мы хотим добавить к адресу ссылки параметр:
export default function ProductList(props) { return ( <ul> {props.products.map(item => ( <li key={item.id}> <Link to={`/catalog/product/${item.id}`}>{item.name}</Link> </li> )} </ul> ); )
Для получения параметров маршрута внутри компонента применяется объект props.match.params
, а для извлечения параметров строки запроса — объект props.location.search
. Во втором случае речь идет о GET-параметрах вида ?one=1&two=2
.
export default function CategoryShow(props){ return <p>Идентификатор категории #{props.match.params.id}</p> }
Поиск: JavaScript • React.js • Web-разработка • Frontend • Маршрутизация • Навигация • Теория • Route • Router • Switch • Link • NavLink • Redirect