浅谈React和Redux的连接react-redux


Posted in Javascript onDecember 04, 2017

之前一直在探索React相关的东西,手上有个SPA项目,于是准备上Redux试试水。Redux本身和React并没有之间的关联,它是一个通用Javscript App模块,用做App State的管理。要在React的项目中使用Redux,比较好的方式是借助react-redux这个库来做连接,这里的意思是,并不是没有react-redux,这两个库就不弄一起用了,而是说react-redux提供了一些封装,一种更科学的代码组织方式,让我们更舒服地在React的代码中使用Redux。

之前仅通过Redux文档来了解react-redux,在一段时间的实践后准备翻一翻源代码,顺便做些相关的总结。我看的代码的npm版本为v4.0.0,也就是说使用的React版本是0.14.x。

react-redux提供两个关键模块:Provider和connect。

Provider

Provider这个模块是作为整个App的容器,在你原有的App Container的基础上再包上一层,它的工作很简单,就是接受Redux的store作为props,并将其声明为context的属性之一,子组件可以在声明了contextTypes之后可以方便的通过this.context.store访问到store。不过我们的组件通常不需要这么做,将store放在context里,是为了给下面的connect用的。

这个是Provider的使用示例:

// config app root
const history = createHistory()
const root = (
 <Provider store={store} key="provider">
  <Router history={history} routes={routes} />
 </Provider>
)

// render
ReactDOM.render(
 root,
 document.getElementById('root')
)

connect

这个模块是算是真正意义上连接了Redux和React,正好它的名字也叫connect。

先考虑Redux是怎么运作的:首先store中维护了一个state,我们dispatch一个action,接下来reducer根据这个action更新state。

映射到我们的React应用中,store中维护的state就是我们的app state,一个React组件作为View层,做两件事:render和响应用户操作。于是connect就是将store中的必要数据作为props传递给React组件来render,并包装action creator用于在响应用户操作时dispatch一个action。

好了,详细看看connect这个模块做了什么。先从它的使用来说,它的API如下:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

mapStateToProps是一个函数,返回值表示的是需要merge进props的state。默认值为() => ({}),即什么都不传。

(state, props) => ({ }) // 通常会省略第二个参数

mapDispatchToProps是可以是一个函数,返回值表示的是需要merge仅props的actionCreators,这里的actionCreator应该是已经被包装了dispatch了的,推荐使用redux的bindActionCreators函数。

(dispatch, props) => ({ // 通常会省略第二个参数
 ...bindActionCreators({
  ...ResourceActions
 }, dispatch)
})

更方便的是可以直接接受一个对象,此时connect函数内部会将其转变为函数,这个函数和上面那个例子是一模一样的。

mergeProps用于自定义merge流程,下面这个是默认流程,parentProps值的就是组件自身的props,可以发现如果组件的props上出现同名,会被覆盖。

(stateProps, dispatchProps, parentProps) => ({
 ...parentProps,
 ...stateProps,
 ...dispatchProps
})

options共有两个开关:pure代表是否打开优化,详细内容下面会提,默认为true,withRef用来给包装在里面的组件一个ref,可以通过getWrappedInstance方法来获取这个ref,默认为false。

connect返回一个函数,它接受一个React组件的构造函数作为连接对象,最终返回连接好的组件构造函数。

然后几个问题:

  1. React组件如何响应store的变化?
  2. 为什么connect选择性的merge一些props,而不是直接将整个state传入?
  3. pure优化的是什么?

我们把connect返回的函数叫做Connector,它返回的是内部的一个叫Connect的组件,它在包装原有组件的基础上,还在内部监听了Redux的store的变化,为了让被它包装的组件可以响应store的变化:

trySubscribe() {
 if (shouldSubscribe && !this.unsubscribe) {
  this.unsubscribe = this.store.subscribe(::this.handleChange)
  this.handleChange()
 }
}

handleChange () {
 this.setState({
  storeState: this.store.getState()
 })
}

但是通常,我们connect的是某个Container组件,它并不承载所有App state,然而我们的handler是响应所有state变化的,于是我们需要优化的是:当storeState变化的时候,仅在我们真正依赖那部分state变化时,才重新render相应的React组件,那么什么是我们真正依赖的部分?就是通过mapStateToProps和mapDispatchToProps得到的。

具体优化的方式就是在shouldComponentUpdate中做检查,如果只有在组件自身的props改变,或者mapStateToProps的结果改变,或者是mapDispatchToProps的结果改变时shouldComponentUpdate才会返回true,检查的方式是进行shallowEqual的比较。

所以对于某个reducer来说:

export default (state = {}, action) => {
 return { ...state } // 返回的是一个新的对象,可能会使组件reRender
 // return state // 可能不会使得组件reRender
}

另外在connect的时候,要谨慎map真正需要的state或者actionCreators到props中,以避免不必要的性能损失。

最后,根据connect的API我们发现可以使用ES7 decorator功能来配合React ES6的写法:

@connect(
 state => ({
  user: state.user,
  resource: state.resource
 }),
 dispatch => ({
  ...bindActionCreators({
   loadResource: ResourceActions.load
  }, dispatch)
 })
)
export default class Main extends Component {

}

OK,以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript中使用构造函数实现继承的代码
Aug 12 Javascript
jquery实现在页面加载的时自动为日期插件添加当前日期
Aug 20 Javascript
JavaScript实现找出数组中最长的连续数字序列
Sep 03 Javascript
原生javascript实现获取指定元素下所有后代元素的方法
Oct 28 Javascript
JS+CSS实现的拖动分页效果实例
May 11 Javascript
jquery html动态添加的元素绑定事件详解
May 24 Javascript
用jQuery向div中添加Html文本内容的简单实现
Jul 13 Javascript
JS轮播图实现简单代码
Feb 19 Javascript
JS实现基于拖拽改变物体大小的方法
Jan 23 Javascript
再谈Angular4 脏值检测(性能优化)
Apr 23 Javascript
JavaScript之Blob对象类型的具体使用方法
Nov 29 Javascript
Node快速切换版本、版本回退(降级)、版本更新(升级)
Jan 07 Javascript
bootstrap3中container与container_fluid外层容器的区别讲解
Dec 04 #Javascript
深入浅出webpack之externals的使用
Dec 04 #Javascript
基于js 字符串indexof与search方法的区别(详解)
Dec 04 #Javascript
基于JavaScript中字符串的match与replace方法(详解)
Dec 04 #Javascript
基于JSONP原理解析(推荐)
Dec 04 #Javascript
利用Javascript获取选择文本所在的句子详解
Dec 03 #Javascript
微信小程序图片选择区域裁剪实现方法
Dec 02 #Javascript
You might like
php 多个submit提交表单 处理方法
2009/07/07 PHP
apache+codeigniter 通过.htcaccess做动态二级域名解析
2012/07/01 PHP
PHP图像处理类库MagickWand用法实例分析
2015/05/21 PHP
php使用redis的有序集合zset实现延迟队列应用示例
2020/02/20 PHP
Thinkphp5框架异常处理操作实例分析
2020/06/03 PHP
jquery实用代码片段集合
2010/08/12 Javascript
js读取本地excel文档数据的代码
2010/11/11 Javascript
jQuery中delegate与on的用法与区别示例介绍
2013/12/20 Javascript
js 实现浏览历史记录示例
2014/04/20 Javascript
jQuery $.each遍历对象、数组用法实例
2015/04/16 Javascript
谈一谈javascript中继承的多种方式
2016/02/19 Javascript
JavaScript 定时器 SetTimeout之定时刷新窗口和关闭窗口(代码超简单)
2016/02/26 Javascript
详解升级react-router 4 踩坑指南
2017/08/14 Javascript
解决Vue2.0 watch对象属性变化监听不到的问题
2018/09/11 Javascript
node.js命令行教程图文详解
2019/05/27 Javascript
自定义javascript验证框架示例【附源码下载】
2019/05/31 Javascript
QML实现圆环颜色选择器
2019/09/25 Javascript
vue实现权限控制路由(vue-router 动态添加路由)
2019/11/04 Javascript
vue实现选中效果
2020/10/07 Javascript
[13:38]2015国际邀请赛中国战队出征仪式
2015/05/29 DOTA
[01:35]辉夜杯战队访谈宣传片—LGD
2015/12/25 DOTA
[54:15]DOTA2-DPC中国联赛 正赛 DLG vs Dragon BO3 第二场2月1日
2021/03/11 DOTA
python使用7z解压软件备份文件脚本分享
2014/02/21 Python
利用Fn.py库在Python中进行函数式编程
2015/04/22 Python
Python实现模拟分割大文件及多线程处理的方法
2017/10/10 Python
python 自定义装饰器实例详解
2019/07/20 Python
pycharm设置python文件模板信息过程图解
2020/03/10 Python
俄罗斯香水和化妆品网上商店:NOTINO.ru
2019/12/17 全球购物
校长就职演讲稿
2014/01/06 职场文书
学子宴答谢词
2014/01/25 职场文书
学校对教师的评语
2014/04/28 职场文书
授权委托书(公民个人适用)
2014/09/19 职场文书
房产协议书范本
2014/10/18 职场文书
2014年人事部工作总结
2014/12/03 职场文书
优秀共产党员推荐材料
2014/12/18 职场文书
管理者们如何制定2019年的工作计划?
2019/07/01 职场文书