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

22.08.2021

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

В React имеется своя система маршрутизации, которая позволяет сопоставлять запросы к приложению с определенными компонентами. Ключевым звеном в работе маршрутизации является модуль react-router, который содержит основной функционал по работе с маршрутизацией. Для браузерных приложений предназначен модуль react-router-dom, для мобильных приложений — react-router-native.

Простое приложение

Давайте создадим простое приложение, у которого будет три страницы — Home, About и Contact:

> npm install react-create-app
[src]
    App.js
    [components]
        Header.js
        Footer.js
        Content.js
    [pages]
        Home.js
        About.js
        Contact.js
import React from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import Content from './components/Content';

export default function App() {
    return (
        <React.Fragment>
            <Header />
            <Content />
            <Footer />
        </React.Fragment>
    );
}
export default function Content() {
    return (
        <main className="container">
            <h1>React Router</h1>
        </main>
    );
}
export default function Home() {
    return <h1>Home page</h1>
}
export default function About() {
    return <h1>About page</h1>
}
export default function Contsct() {
    return <h1>Contact page</h1>
}

Компоненты Router и Route

Теперь установим модуль react-router-dom:

> npm install react-router-dom

Нам потребуется импортировать BrowserRouter и Route:

import {BrowserRouter as Router, Route} from 'react-router-dom';
import Home from '../pages/Home';
import About from '../pages/About';
import Contact from '../pages/Contact';

export default function Content() {
    return (
        <main className="container">
            <Router>
                <Route path="/" component={Home} />
                <Route path="/about" component={About} />
                <Route path="/contact" component={Contact} />
            </Router>
        </main>
    );
}

Здесь присходит следующее — перебираются все элементы Route внутри Router и если есть совпадение текущего URL с атрибутом path — вызывается компонент, который указан в атрибуте component. Запустим наше приложение:

> npm start

И увидим на главной странице результат работы компонента Home:

<h1>Home page</h1>

Атрибуты exact и strict

Наберем в адресной строке /about — и увидим сразу два заголовка:

<h1>Home page</h1>
<h1>About page</h1>

Атрибут path — это шаблон, который означает «URL страницы начинается с этой строки». То есть, если path="/about", то этому шаблону будут соответствовать URL-ы /about, /about/one, /about/one/two — потому что все они начинаются на /about.

В нашем случае произошло следующее. У главной страницы path — это слэш, а наш URL в адресной строке браузера — это /about. Поскольку URL начинается со слэша, то шаблон path="/" срабатывает. Чтобы такого не происходило, можно использовать атрибуты exact (точный) и strict (строгий).

export default function Content() {
    return (
        <main className="container">
            <Router>
                <Route exact path="/" component={Home} />
                <Route path="/about" component={About} />
                <Route path="/contact" component={Contact} />
            </Router>
        </main>
    );
}

Атрибут exact требует точного совпадения текущего URL и шаблона path. Теперь путь «слэш» будет соответствовать только URL главной страницы. Но у нас по прежнему работают URL /about/one и /about/one/two — давайте исправим это.

export default function Content() {
    return (
        <main className="container">
            <Router>
                <Route exact path="/" component={Home} />
                <Route exact path="/about" component={About} />
                <Route exact path="/contact" component={Contact} />
            </Router>
        </main>
    );
}

Сейчас страница About (и страница Contact) доступна по двум адресам — это /about и /about/. Атрибут strict позволяет избавиться от дублей.

export default function Content() {
    return (
        <main className="container">
            <Router>
                <Route exact path="/" component={Home} />
                <Route exact strict path="/about" component={About} />
                <Route exact strict path="/contact" component={Contact} />
            </Router>
        </main>
    );
}
Есть еще атрибут sensetive, который делает шаблон чувствительным к регистру. То есть, если path="/about", то URL /About уже не будет совпадать.

Компонент Switch

Мы уже знаем, что внутри Router перебираются все элементы Route — даже если совпадение уже найдено. Но мы можем изменить это поведение с помощью Switch, который позволяет выйти из перебора при первом совпадении.

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';

export default function Content() {
    return (
        <main className="container">
            <Router>
                <Switch>
                    <Route exact path="/" component={Home} />
                    <Route exact strict path="/about" component={About} />
                    <Route exact strict path="/contact" component={Contact} />
                </Switch>
            </Router>
        </main>
    );
}

Компонент NotFound

Осталась еще одна проблема. Если не было совпадений при переборе маршрутов — пользователь увидит пустую страницу. Так что давайте создадим еще один компонент NotFound.

export default function NotFound() {
    return <h1>Page not found</h1>
}
export default function Content() {
    return (
        <main className="container">
            <Router>
                <Switch>
                    <Route exact path="/" component={Home} />
                    <Route exact strict path="/about" component={About} />
                    <Route exact strict path="/contact" component={Contact} />
                    <Route component={NotFound} />
                </Switch>
            </Router>
        </main>
    );
}

Здесь присходит следующее — перебираются все элементы Route внутри Router до первого совпадения текущего URL с атрибутом path. Если совпадений не было — отработает компонент NotFound.

Компоненты Link и NavLink

Все это хорошо, но мы набираем адрес страницы вручную в адресной строке браузера, а хотелось бы кликать по ссылкам. Давайте создадим компонент NavBar, в котором у нас будут ссылки, но не простые ссылки, а особенные.

import {Link} from 'react-router-dom';

export default function NavBar() {
    return (
        <nav>
            <Link to="/">Home</Link>
            <Link to="/about">About</Link>
            <Link to="/contact">Contact</Link>
        </nav>
    )
}
export default function Content() {
    return (
        <main className="container">
            <Router>
                <NavBar />
                <Switch>
                    <Route exact path="/" component={Home} />
                    <Route exact strict path="/about" component={About} />
                    <Route exact strict path="/contact" component={Contact} />
                    <Route component={NotFound} />
                </Switch>
            </Router>
        </main>
    );
}

Кроме компонента Link для создания ссылок можно использовать компонент NavLink. Главное отличние NavLink от Link — возможность задать css-класс и/или css-стиль активной ссылки. Как нетрудно догадаться из названия, это может быть полезно для главного меню, чтобы пользователю было удобнее.

import {NavLink} from 'react-router-dom';

export default function NavBar() {
    const activeStyle = {color: 'red'};
    return (
        <nav>
            <NavLink exact to="/" activeStyle={activeStyle}>Home</NavLink>
            <NavLink to="/about" activeStyle={activeStyle}>About</NavLink>
            <NavLink to="/contact" activeStyle={activeStyle}>Contact</NavLink>
        </nav>
    )
}
import {NavLink} from 'react-router-dom';

export default function NavBar() {
    return (
        <nav>
            <NavLink exact to="/" activeClassName="active">Home</NavLink>
            <NavLink to="/about" activeClassName="active">About</NavLink>
            <NavLink to="/contact" activeClassName="active">Contact</NavLink>
        </nav>
    )
}
.active {
    color: red;
}

Тут тоже не надо забывать об атрибуте exact — иначе ссылка Home будет красной всегда.

Главное отличие Link и NavLink от простых ссылок — это то, что каждая новая страница не загружается с сервера. Собственно, поэтому приложение и называется Single Page Application — потому что страница загружается один раз, при первом обращении. А дальше мы только точечно изменяем ее содержимое, показывая внутри Content то одно содержимое, то другое — без обращения к серверу. Но для пользователя это выглядит, как будто мы каждый раз показываем новую страницу. В адресной строке браузера показывается соответствующий URL, правильно работают кнопки «Назад» и «Вперед» браузера.

Атрибут basename

Если приложение развернуто в подкаталоге на сервере, необходимо использовать атрибут basename для Router:

<Router basename="/calendar">
    <Link to="/yesterday" />
    <Link to="/today" />
    <Link to="/tomorrow" />
</Router>
<Router basename="/calendar">
    <Route path="/yesterday" component={...} />
    <Route path="/today" component={...} />
    <Route path="/tomorrow" component={...} />
</Router>

Поиск: 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.