React.js. Маршрутизация. Часть вторая из трех

23.08.2021

Теги: FrontendJavaScriptReact.jsWeb-разработкаМаршрутизацияНавигацияТеория

Дочерние маршруты

В рамках маршрутов в 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>&nbsp;
            <NavLink to="/catalog" activeClassName="active">Catalog</NavLink>&nbsp;
            <NavLink to="/about" activeClassName="active">About</NavLink>&nbsp;
            <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

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.