• 原文地址:The Love-Hate Relationship between React Router and React Components
  • 原文作者:Kasra
  • 译文出自:掘金翻译计划
  • 本文永久链接:github.com/xitu/gold-m…
  • 译者:Augustwuli
  • 校对者:RicardoCao-Biker

来源:谷歌图片

作为 React 开发者,我们大部分人享用着使用 React Router 为 React 应用的路由带来的便利。

为什么我们 ❤️ React 路由:

  • 与 React 完美结合并且遵循相同的原则
  • 路由的导航方面非常容易理解
  • 组件组合、声明性 UI、状态管理 并且它紧密地追随着 React 的工作流 (事件 => 状态变化 => 重新渲染)
  • 可靠的 浏览历史特征 允许用户在追踪视图状态的同时在应用中导航。

然而在使用 React 路由的时候,如果你的应用程序特定需求变得比你在 web 上的每个教程中看到的常规用法稍微复杂一些,你将面对一些困难。

好消息是即使在那些场景下,React 路由仍然允许我们以一种干净的方式解决问题;但是解决方案可能并不像一眼能开出来那么明显。这儿有个我们在 Fjong 开发团队 ? 的案例,我们在路由路径改变查询参数并且期望一个组件被重新渲染,React Router 的表现却不是那么回事儿。

在我们描述具体问题和我们如何解决这个问题之前,让我们聊聊 React 路由和 React 组件之间巨大关系的几个方面。

相爱关系

React 路由和 React 组件之间有很多的联系。这主要是因为它们都遵循上面提到的相同的事件循环 (事件 => 状态变化 => 重新渲染)。现在记住这个流程,我们将解决在应用程序中导航的一个常见问题;当路由更改的时候滚动到页面的顶部

假设你有一组名为 HomeAboutSearch 的组件

<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 的时候,你需要滚动很多次才能在 Search 页面看到你想看到的项目。

然后,你在地址栏输入跳转至 /about 的链接,然后突然看到了 About Us 页面的底部,而不是顶部,这可能很烦人。这有一些方法解决这个问题,但是 React 路由为你提供了所有必要的工具来正确地完成这个任务。让我们来看看实际情况。

/* 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 路由和 React 组件的情况相同。为了更好地理解这一点,我们来看看应用程序中的一个可能的场景。

假设你要从 /search/about,然后当你到达 About Us 页面时,页面显然会像你所期望的那样重新渲染。从 /about 导航到 /search 也是如此。

现在假设从 /search?tags=Dresses/search?tags=Bags 的时候,你的 SearchPage 将搜索查询参数附加到 URL 上,并且你希望重新渲染这些参数。在这,我们更改了 React 路由路径 location.path = /search 上的搜索查询,它被 React 路由识别为同一位置对象上的属性 location.search = ?tags=Dresses or ?tags=Bags

无论是 React 路由还是你的组件都没有意识到它们需要重新渲染页面,因为从技术上讲,我们还是在同一个页面。React 组件不允许在相同路径但是不同搜索查询间的路由跳转触发重新渲染。

目前我们的路由和组件似乎有点脱节。好难过 :(

所以,我们如何才能解决这个问题呢?其实他们每个人都有解决这个问题的方法。React 路由告诉我们 URL 中的搜索查询参数是否发生了变化而且更重要的是根据 React 正确的生命周期来做这件事。之后,组件将负责决定如何处理这些信息。

在这个案例中,如果组件需要重新渲染(由一个叫 RouteKey 的 boolean 属性(prop)决定)它将向组件传递一个唯一的键,该键是 location.pathnamelocation.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 路由和组件完美结合的例子,以及它们稍微分离时的场景。但是重要的是要记住,在大部分情况下,React 路由遵循和 React 相同的原则和设计模式,花时间熟悉这些原则及其相关的执行上下文,对于在 React 路由中修复 bug 会有很大帮助。

如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。

[译] React 路由和 React 组件的爱恨情仇相关推荐

  1. 本体开发日记07-我与java分词组件的爱恨情仇

    OS:吐槽一下,我的破电脑,昨天对于github上下载的那个jieba命名用不了,现在,一晚上,我今天能用了!!千年虫是不是依靠重启和等待就可以解决了!关键是,我的电脑他自己一晚上把这个程序自己解决了 ...

  2. 本体开发日记07-我与java分词组件的爱恨情仇-Hit类

    先是几个参数:UNMATCH MATCH PREFIX hitState begin end matchedDictSegment //Hit不匹配 private static final int ...

  3. 初识angular.js之爱恨情仇

    angular.js Angular.JS 是一组用来开发Web页面的框架.模板以及数据绑定和丰富UI组件.它支持整个开发进程,提供web应用的架构,无需进行手工DOM操作. AngularJS很小, ...

  4. 那些大学简称背后的“爱恨情仇”:东西南北中,就剩北大没人抢了

    春节期间,免不了要被亲戚问在哪读大学? >>>> 我:华中大啦 亲戚:?有这大学 我:华中科技大学 亲戚:奥~华科啊,华科就华科,华中大是啥啊 我:委屈啊,学校官方简称是华中大 ...

  5. 一个程序员的人情世故、爱恨情仇

    摘要: 提起程序员这职业,很多人的眼里大多数都会浮现一个词 - - - 闷葫芦,有的程序员就会反驳了,放你娘的狗屁!程序员也会有浪漫的,也会有别人没有达得到的高度好吧!无疑包括人情世故.爱恨情仇! 你 ...

  6. Spring发展史! 和那些巨头的爱恨情仇!

    ** 码农的春天----------Spring来了 关于Spring的发展起源要回溯到2002年,当时正是Java EE和EJB大行其道的时候,很多知名公司都是采用此技术方案进行项目开发.这时候有一 ...

  7. 产品经理和程序员的爱恨情仇

    产品经理跪求程序员,程序员跪求程序成功上线! 前几天纯银V在微博上发了一条微博「很多人吐槽"人人都是产品经理"这句话,其实在我看来,这句话的正确理解是"人人都应该学习产品 ...

  8. 深度解析单例与序列化之间的爱恨情仇

    转载自 深度解析单例与序列化之间的爱恨情仇 本文将通过实例+阅读Java源码的方式介绍序列化是如何破坏单例模式的,以及如何避免序列化对单例的破坏. 单例模式,是设计模式中最简单的一种.通过单例模式可以 ...

  9. mysql 唯一索引 死锁_MySQL 死锁套路:唯一索引 S 锁与 X 锁的爱恨情仇

    毫不夸张的说,有一半以上的死锁问题由唯一索引贡献,后面介绍的很多死锁的问题都跟唯一索引有关.这次我们讲一段唯一索引 S 锁与 X 锁的爱恨情仇 我们来看一个简化过的例子 # 构造数据 CREATE T ...

最新文章

  1. mybatis 思维导图,让 mybatis 不再难懂(二)
  2. 干货 | Elasticsearch开发人员最佳实战指南
  3. 为什么说没有物联网,就没有 AI ?
  4. Qt5.12 制作串口调试助手
  5. Bernoulli-Gaussian分布
  6. 皖能合肥电厂电能量计量管理系统设计方案
  7. Python常用模块 之 hashlib模块
  8. 浅谈prometheus(普罗米修斯) client golang
  9. 2021 年年度蕞佳开源软件!
  10. POJ 1389 Area of Simple Polygons(扫描线求面积)
  11. 2.数据的结构 (学校考试必考概念)-数据结构入门(c语言实现)
  12. 信息收集的方法有哪些
  13. 路由器和网络互连实验
  14. Swift 函数参数前的“_”是什么意思?
  15. 【Arduino】控制A4950的循迹小车设计
  16. 利用超级电容给系统供电真的靠谱吗?
  17. 微信一笔画游戏 的 路径算法
  18. win7系统开机黑屏无任何图标的解决方法
  19. PEiD的基础使用(持续--)
  20. 量化投资里的风险收益分析与可视化:empyrical和pyfolio实战,与backtrader整合

热门文章

  1. 你现在还在使用刷脸支付吗?不,刷手支付已来!!!不侵犯隐私、秒速支付...
  2. java linux cpu 多核 负载不均匀,系统CPU负载过高、CPU使用率不高的问题
  3. 完胜BERT!NLP预训练利器:小模型也有高精度,单个GPU就能训练
  4. 武汉大学提出ARGAN:注意力循环生成对抗模型用于检测、去除图像阴影 | ICCV 2019
  5. 人脸识别是怎么识别的?为什么需要大数据?(原理篇)
  6. 马斯克说,特斯拉最新自动驾驶芯片性能是英伟达的7倍​
  7. 倪光南:下一次科技革命集中在人工智能等三方面
  8. 干货丨各种机器学习任务的顶级结果(论文)汇总
  9. 干货丨 一文概览深度学习中的激活函数
  10. 第一次,人类在人工神经网络中发现了“真”神经元