От автора: как разработчикам 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.