Содержит сущности для роутинга в React.
Подключите этот пакет в зависимости:
npm i -S @devim-front/react-route
Библиотека представляет собой надстройку над react-router-dom, призванную решить несколько распространённых проблем этого механизма.
Центральным понятием библиотеки является маршрут. Маршрут - это ленивое хранилище, призванное типизировать и унифицировать процесс маршрутизации в приложениях на react и mobx.
В библиотеке react-router-dom
за обработку адресов страниц отвечает компонент Route. Среди его свойств есть свойство path, которое представляет собой маску адреса страницы и component - обработчик адреса, то есть компонент, который будет отображен, когда адрес страницы совпадает с маской. Этот подход дает большую степень свободы, но в больших проектах всплывают несколько проблем:
-
Когда в масках используются параметры (например, "id" в маске "/article/:id" - см. документацию), в Typescript нет возможности покрыть их типами.
-
Библиотека способна сопоставить маску и адрес, но не предоставляет инструментов ни для генерации адресов по маске, ни для парсинга иных адресов, кроме текущего.
Маршрут из нашей библиотеки призван расширить функционал библиотеки react-router-dom, сохранив при этом совместимость с ней. Предполагается, что любому типовому адресу страницы соответствует собственный класс маршрута. Он выглядит приблизительно так:
// HomeRoute.ts
import { Route } from '@devim-front/react-route';
import { HomePage } from './HomePage';
export class HomeRoute extends Route {
public path = '/';
public component = HomePage;
}
В свойствах класса объявляется маска адреса этого маршрута и компонент, который будет его обрабатывать. Если маска нашего маршрута имеет параметры, то их следует объявить в generic маршрута:
// ArticleRoute.ts
import { Route } from '@devim-front/react-route';
import { ArticlePage } from './ArticlePage';
type Params = {
id: string;
};
export class ArticleRoute extends Route<Params> {
public path = '/article/:id';
public component = ArticlePage;
}
Чтобы задействовать маршруты в приложении, используется метод render
:
import React from 'react';
import { BrowserRouter, Switch } from 'react-router-dom';
import { ArticleRoute } from './ArticleRoute';
import { HomeRoute } from './HomeRoute';
export const App = () => (
<BrowserRouter>
<Switch>
{ArticleRoute.get().render()}
{HomeRoute.get().render()}
</Switch>
</BrowserRouter>
);
Метод render
возвращает элемент Route
из библиотеки react-router-dom
, присваивая ему свойства path
и component
. В нашем примере, если адрес страницы будет соответствовать маске из ArticleRoute
, будет отображен компонент ArticlePage
. Иначе - HomePage
.
Если для генерации ссылок из масок маршрутов используется метод href
. Сгенерируем ссылку на статью в нашем примере:
import { ArticleRoute } from './ArticleRoute';
const href = ArticleRoute.get().href({ id: '1' });
// -> /article/1
С помощью метода parse
можно разбирать любые адреса:
const params = ArticleRoute.get().parse('/article/1');
// -> { id: '1' }
Проверка, соответствует ли адрес маршруту, выполняется методом isMatch
:
const isMatch = ArticleRoute.get().isMatch('/article/2');
// -> true
Итак, допустим, мы объявили маршрут и задействовали его в приложении. Как нам теперь его использовать? В библиотеке react-router-dom
предлагается HOC withRouter или коллекция хуков. Этот подход имеет те же проблемы, которые мы описали выше: нет унификации и чёткой типизации.
Наш маршрут, являясь ленивым хранилищем, имеет observable-свойства isActive
и params
.
isActive
- флаг, указывающий, совпадает ли текущий адрес с маршрутом. params
- типизированная коллекция значений параметров, полученных при разборе текущего адреса по маске:
// ArticlePage.tsx
import React from 'react';
import { observer } from 'mobx-react';
import { ArticleRoute } from './ArticleRoute';
export const ArticlePage = observer(() => {
const { id } = ArticleRoute.get().params;
return <h1>Here is article {id}</h1>;
});
Рекомендуется добавлять в класс маршрута computed-свойства, соответствующие параметрам:
// ArticleRoute.ts
import { Route } from '@devim-front/react-route';
import { computed } from 'mobx';
import { ArticlePage } from './ArticlePage';
type Params = {
id: string;
};
export class ArticleRoute extends Route<Params> {
public path = '/article/:id';
public component = ArticlePage;
@computed
public get id() {
return this.params.id;
}
}
Тогда их применение становится проще:
// ArticlePage.tsx
import React from 'react';
import { observer } from 'mobx-react';
import { ArticleRoute } from './ArticleRoute';
export const ArticlePage = observer(() => {
const { id } = ArticleRoute.get();
return <h1>Here is article {id}</h1>;
});
Чтобы механизм observable заработал, нужно связать его с react-router-dom
. За это отвечает компонент маршрутизатора Router
, предоставляемый нашей библиотекой. Но делает он далеко не только это.
Большинство react-приложений строятся таким образом, чтобы работать не только в браузере, но и на NodeJS для Server Side Rendering. Библиотека react-router-dom
предоставляет свои маршрутизаторы: StaticRouter
для серверной части и BrowserRouter
для клиентской. Мы решили объединить их в один компонент Router
:
// index.ts
import { Router } from '@devim-front/react-route';
import { render } from 'react-dom';
import { App } from './App';
render(<Router application={App} />, document.getElementById('root'));
Он сам определяет, в какой среде выполняется приложение, и использует соответствующий контекст. Он предоставляет унифицированный набор свойств, совместимый со всеми возможностями компонентов из react-router-dom
.
Кроме того, в роутере можно объявить, какой компонент должен быть отображён, если ни один из маршрутов не обработал текущую страницу. Сделать это можно с помощью свойства notFound
:
// index.ts
import { Router } from '@devim-front/react-route';
import { render } from 'react-dom';
import { NotFound } from './NotFound';
import { App } from './App';
render(
<Router application={App} notFound={NotFound} />,
document.getElementById('root')
);
Для работоспособности всех фич библиотеки мы настоятельно рекомендуем использовать компонент Router
вместо стандартных средств react-router-dom
.
Документация находится в этом разделе.