Отношения любовь-ненависть между React Router и React Components

Отношения любовь-ненависть между React Router и React Components

От автора: как разработчикам React, большинству из нас нравится работать с React Router и нравится то, насколько хорошо он подходит для нашего приложения React.

Почему мы любим React Router:

Прекрасно работает с React и следует тем же принципам

Навигационный аспект маршрутизатора очень просто понять

Компонентный состав, декларативный пользовательский интерфейс, управление состояниями и то, насколько он следует основному потоку React (событие => изменение состояния => ререндеринг)

Надежная функция истории браузинга, которая позволяет пользователям перемещаться по всему приложению, отслеживая состояние просмотра

Тем не менее, вы столкнетесь с некоторыми проблемами при использовании React Router, если потребности вашего приложения станут немного более сложными, чем обычные примеры использования, которые можно найти в любом учебном пособии в Интернете.

Хорошей новостью является то, что даже при этих сценариях React Router по-прежнему позволяет нам решать проблемы чистым способом; но решение может быть не столь очевидным на первый взгляд. Это случилось с нашей командой Fjong, когда мы изменяли параметры запроса по маршруту и ожидали, что будет выполнен реререндеринг компонента, но это не происходило.

Прежде чем мы рассмотрим эту конкретную проблему и как мы ее разрешили, давайте поговорим о нескольких аспектах великих отношений между React Router и React Components.

Отношения любви

У React Router и React Components отличные отношения. Это в основном это касается шаблона, в котором оба они следуют одному и тому же циклу событий, о нем говорилось выше (событие => изменение состояния => ререндеринг). Теперь, имея в виду этот поток, мы собираемся исправить общую проблему при навигации по приложению; прокрутка вверх страницы при изменении маршрута.

Представьте, что у вас есть набор компонентов с именами Home, About и Search.

<Router history={History}> <Switch> <Route exact path="/" component={Home}/> <Route exact path="/about" component={About}/> <Route exact path="/search" component={Search}/> <Route exact component={NoMatch}/> </Switch>
</Router>

Теперь представьте, что когда вы переходите к /search, вам нужно выполнить несколько прокруток, чтобы увидеть ваш любимый элемент на SearchPage.

Затем вы вводите ссылку в заголовке, чтобы перейти к /about, а затем вдруг вы видите нижнюю часть страницы About Us — вместо верхней части, что весьма раздражает. Существует много способов решить эту проблему, но React Router предоставляет вам все необходимые инструменты для выполнения этой задачи, чистые и правильные. Давайте рассмотрим это в действии.

/* globals window */ /* Global Dependencies */
const React = require('react');
const { Component } = require('react');
const PropTypes = require('prop-types');
const { Route, withRouter } = require('react-router-dom'); class ScrollToTopRoute extends Component { componentDidUpdate(prevProps) { if (this.props.location.pathname !== prevProps.location.pathname) { window.scrollTo(0, 0); } } render() { const { component: Component, ...rest } = this.props; return <Route {...rest} render={props => (<Component {...props} />)} />; }
} ScrollToTopRoute.propTypes = { path: PropTypes.string, location: PropTypes.shape({ pathname: PropTypes.string, }), component: PropTypes.instanceOf(Component),
}; module.exports = withRouter(ScrollToTopRoute); // Usage in App.jsx
<Router history={History}> <Switch> <ScrollToTopRoute exact path="/" component={Home}/> <ScrollToTopRoute exact path="/about" component={About}/> <ScrollToTopRoute exact path="/search" component={Search}/> <ScrollToTopRoute exact component={NoMatch}/> </Switch>
</Router>

Ненависть

Но как и в любых отношениях, в отдельных ситуациях все идет не так хорошо. То же касается и React Router и React Components. Чтобы лучше понять это, давайте рассмотрим возможный сценарий в приложении.

Представьте, что вы переходите от /search к /about, и когда вы переходите к About Us, страница, очевидно, повторно отображается, как и следовало ожидать. То же самое происходит при переходе от /about к /search.

Теперь представьте, что ваш SearchPage привязывает параметры поискового запроса к URL-адресу, и вы ожидаете ререндеринга при переходе с /search?tags=Dresses на /search?tags=Bags. Здесь мы меняем поисковый запрос на путь ReactRouter location.path=/search, который распознается ReactRouter, как свойство в том же месте, что и location.search = ?tags=Dresses or ?tags=Bags.

Ни React Router, ни ваш компонент не понимают, что им нужно выполнить ререндеринг, потому что технически мы находимся на одной странице. React Component не согласен с вами в том, что при переходе между двумя маршрутами, которые находятся по одному пути, но имеют разные поисковые запросы, нужно выполнять ререндеринг.
На данный момент наши Route и Component немного обособлены. Какая жалость :(

Итак, как мы можем исправить эту проблему? Ну, оказывается, каждый из них содержит кусочек головоломки, разгадав которую, мы можем исправить проблему. React Router может сообщить нам, изменились ли параметры поискового запроса в URL-адресе и что более важно сделать это в соответствии с нужными событиями жизненного цикла React. После этого Component решает, что делать с этой информацией.

В этом случае, если компоненту нужен ререндеринг (указывается логическим свойством, называемым RouteKey), он передает компоненту уникальный ключ, который будет представлять собой комбинацию location.pathname и location.search. (Это общее правило для ключей, которые должны быть уникальными, стабильными и предсказуемыми). В этом случае компонент получает новый ключ каждый раз, когда запрашивается маршрут; и даже, если вы остаетесь на одной странице, он будет повторно отображать страницу без каких-либо побочных эффектов. Посмотрим, как это работает на практике!

/* globals window */ /** Global Dependencies */
const React = require('react');
const { Component } = require('react');
const PropTypes = require('prop-types');
const { Route, withRouter } = require('react-router-dom'); class ScrollToTopRoute extends Component { componentDidUpdate(prevProps) { if (this.props.location.pathname !== prevProps.location.pathname) { window.scrollTo(0, 0); } } render() { const { component: Component, RouteKey, location, ...rest } = this.props; /** * Sometimes we need to force a React Route to re-render when the * search params (query) in the url changes. React Router does not * do this automatically if you are on the same page when the query * changes. By passing the `RouteKey`ro the `ScrollToTopRoute` and * setting it to true, we are passing the combination of pathname and * search params as a unique key to the component and that is a enough * and clear trigger for the component to re-render without side effects */ const Key = RouteKey ? location.pathname + location.search : null; return <Route {...rest} render={props => (<Component {...props} key={Key} />)} />; }
} ScrollToTopRoute.propTypes = { path: PropTypes.string, location: PropTypes.shape({ pathname: PropTypes.string, }), component: PropTypes.instanceOf(Component), RouteKey: PropTypes.boolean,
}; module.exports = withRouter(ScrollToTopRoute); // Usage in App.jsx
<Router history={History}> <Switch> <ScrollToTopRoute exact path="/" component={Home}/> <ScrollToTopRoute exact path="/about" component={About}/> <ScrollToTopRoute exact path="/search" component={Search} RouteKey={true} /> <ScrollToTopRoute exact component={NoMatch}/> </Switch>
</Router>

Заключение

Мы рассмотрели примеры того, как React Router и React Component работают вместе идеально, а также сценарии, когда они немного отстранены друг от друга. Но самое главное помнить, что в большинстве случаев React Router следует тем же принципам и шаблонам проектирования, что и React, и, ознакомившись ближе с этими принципами и контекстами их выполнения, вы сможете эффективнее исправлять ошибки в React Router.

Автор: Kasra

Источник: https://blog.bitsrc.io/

Редакция: Команда webformyself.