React学习笔记_react-redux原理分析
2017-11-01 16:06
661 查看
react-redux原理分析
更多干货
分布式实战(干货)spring cloud 实战(干货)
mybatis 实战(干货)
spring boot 实战(干货)
React 入门实战(干货)
构建中小型互联网企业架构(干货)
写在前面之前写了一篇分析Redux中Store实现的文章(详见:Redux原理(一):Store实现分析),突然意识到,其实React与Redux并没有什么直接的联系。Redux作为一个通用模块,主要还是用来处理应用中state的变更,而展示层不一定是React。
但当我们希望在React+Redux的项目中将两者结合的更好,可以通过react-redux做连接。
本文结合react-redux的使用,分析其实现原理。
react-redux
react-redux是一个轻量级的封装库,核心方法只有两个:Providerconnect
下面我们来逐个分析其作用
Provider
完整源码请戳这里Provider模块的功能并不复杂,主要分为以下两点:在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
接收Redux的store作为props,通过context对象传递给子孙组件上的connect
下面看下具体代码:
封装原应用
[31-34] render方法中,渲染了其子级元素,使整个应用成为Provider的子组件。1、
this.props.children是react内置在
this.props上的对象,用于获取当前组件的所有子组件
2、
Children为react内部定义的顶级对象,该对象上封装了一些方便操作子组件的方法。
Children.only用于获取仅有的一个子组件,没有或超过一个均会报错。故需要注意:确保Provider组件的直接子级为单个封闭元素,切勿多个组件平行放置。
传递store
[26-29] Provider初始化时,获取到props中的store对象;[22-24] 将外部的store对象放入context对象中,使子孙组件上的connect可以直接访问到context对象中的store。
1、
context可以使子孙组件直接获取父级组件中的数据或方法,而无需一层一层通过props向下传递。
context对象相当于一个独立的空间,父组件通过
getChildContext()向该空间内写值;定义了
contextTypes验证的子孙组件可以通过
this.context.xxx,从context对象中读取xxx字段的值。
小结
总而言之,Provider模块的功能很简单,从最外部封装了整个应用,并向connect模块传递store。而最核心的功能在connect模块中。
connect
正如这个模块的命名,connect模块才是真正连接了React和Redux。现在,我们可以先回想一下Redux是怎样运作的:首先需要注册一个全局唯一的store对象,用来维护整个应用的state;当要变更state时,我们会dispatch一个action,reducer根据action更新相应的state。
下面我们再考虑一下使用react-redux时,我们做了什么:
import React from "react" import ReactDOM from "react-dom" import { bindActionCreators } from "redux" import {connect} from "react-redux" class xxxComponent extends React.Component{ constructor(props){ super(props) } componentDidMount(){ this.props.aActions.xxx1(); } render ( <div> {this.props.$$aProps} </div> ) } export default connect( state=>{ return { $$aProps:state.$$aProps, $$bProps:state.$$bProps, // ... } }, dispatch=>{ return { aActions:bindActionCreators(AActions,dispatch), bActions:bindActionCreators(BActions,dispatch), // ... } } )(xxxComponent)通过以上代码,我们可以归纳出以下信息:1、使用了react-redux后,我们导出的对象不再是原先定义的xxxComponent,而是通过connect包裹后的新React.Component对象。
connect执行后返回一个函数(wrapWithConnect),那么其内部势必形成了闭包。而wrapWithConnect执行后,必须要返回一个ReactComponent对象,才能保证原代码逻辑可以正常运行,而这个ReactComponent对象通过render原组件,形成对原组件的封装。
2、渲染页面需要store tree中的state片段,变更state需要dispatch一个action,而这两部分,都是从
this.props获取。故在我们调用connect时,作为参数传入的state和action,便在connect内部进行合并,通过props的方式传递给包裹后的ReactComponent。
好了,以上只是我们的猜测,下面看具体实现,完整代码请戳这里。
connect完整函数声明如下:
connect( mapStateToProps(state,ownProps)=>stateProps:Object, mapDispatchToProps(dispatch, ownProps)=>dispatchProps:Object, mergeProps(stateProps, dispatchProps, ownProps)=>props:Object, options:Object )=>( component )=>component再来看下connect函数体结构,我们摘取核心步骤进行描述
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) { // 参数处理 // ... return function wrapWithConnect(WrappedComponent) { class Connect extends Component { constructor(props, context) { super(props, context) this.store = props.store || context.store; const storeState = this.store.getState() this.state = { storeState } } // 周期方法及操作方法 // ... render(){ this.renderedElement = createElement(WrappedComponent, this.mergedProps //mearge stateProps, dispatchProps, props ) return this.renderedElement; } } return hoistStatics(Connect, WrappedComponent); } }其实已经基本印证了我们的猜测:
1、connect通过context获取Provider中的store,通过store.getState()获取整个store tree 上所有state。
2、connect模块的返回值wrapWithConnect为function。
3、wrapWithConnect返回一个ReactComponent对象Connect,Connect重新render外部传入的原组件WrappedComponent,并把connect中传入的mapStateToProps, mapDispatchToProps与组件上原有的props合并后,通过属性的方式传给WrappedComponent。下面我们结合代码进行分析一下每个函数的意义。
mapStateToProps
mapStateToProps(state,props)必须是一个函数。
参数state为store tree中所有state,参数props为通过组件Connect传入的props。
返回值表示需要merge进props中的state。
以上代码用来计算待merge的state,[104-105]通过调用
finalMapStateToProps获取merge state。其中作为参数的state通过
store.getState()获取,很明显是store tree中所有的state
mapDispatchToProps
mapDispatchToProps(dispatch, props)可以是一个函数,也可以是一个对象。
参数dispatch为
store.dispatch()函数,参数props为通过组件Connect传入的props。
返回值表示需要merge进props中的action。
以上代码用来计算待merge的action,代码逻辑与计算state十分相似。作为参数的
dispatch就是
store.dispatch。
mergeProps
mergeProps是一个函数,定义了
mapState,
mapDispatch及
this.props的合并规则,默认合并规则如下:
需要注意的是:如果三个对象中字段出现同名,前者会被后者覆盖
如果通过connect注册了
mergeProps方法,以上代码会使用
mergeProps定义的规则进行合并,
mergeProps合并后的结果,会通过props传入Connect组件。
options
options是一个对象,包含
pure和
withRef两个属性
pure
表示是否开启pure优化,默认值为truewithRef
withRef用来给包装在里面的组件一个ref,可以通过getWrappedInstance方法来获取这个ref,默认为false。React如何响应store变化
文章一开始我们也提到React其实跟Redux没有直接联系,也就是说,Redux中dispatch触发store tree中state变化,并不会导致React重新渲染。react-redux才是真正触发React重新渲染的模块,那么这一过程是怎样实现的呢?
刚刚提到,connect模块返回一个wrapWithConnect函数,wrapWithConnect函数中又返回了一个Connect组件。Connect组件的功能有以下两点:
1、包装原组件,将state和action通过props的方式传入到原组件内部
2、监听store tree变化,使其包装的原组件可以响应state变化
下面我们主要分析下第二点:
如何注册监听
Redux中,可以通过store.subscribe(listener)注册一个监听器。listener会在store tree更新后执行。
以上代码为Connect组件内部,向store tree注册listener的过程。
[199] 调用
store.subscribe注册一个名为
handleChange的listener,返回值为当前listener的注销函数。
何时注册
可以看到,当Connect组件加载到页面后,当前组件开始监听store tree变化。
何时注销
当当前Connect组件销毁后,我们希望其中注册的listener也一并销毁,避免性能问题。此时可以在Connect的componentWillUnmount周期函数中执行这一过程。
变更处理逻辑
有了触发组件更新的时机,我们下面主要看下,组件是通过何种方式触发重新渲染[244-245] Connect组件在初始化时,就已经在
this.state中缓存了store tree中state的状态。这两行分别取出当前state状态和变更前state状态进行比较
[262] 比较过程暂时略过,这一行将最终store tree中state通过
this.setState()更新到Connect内部的state中,而
this.setState()方法正好可以触发Connect及其子组件的重新渲染。
小结
可以看到,react-redux的核心功能都在connect模块中,理解好这个模块,有助于我们更好的使用react-redux处理业务问题,优化代码性能。总结
本文通过分析react-redux源码,详细介绍了Provider和connect模块,重新梳理了Reat、redux、react-redux三者间的关系。相关文章推荐
- 学习笔记1:《大型网站技术架构 核心原理与案例分析》之 大型网站软件系统的特点
- 编译原理学习笔记05——(识别孙悟空72变之魔鬼特训—递归下降分析程序构造)——2014_1_20
- 逆向工程核心原理学习笔记(十三):分析abex' crackme #1 的延伸:将参数压入栈
- React学习笔记--通过Redux 的三个基本原则来理解Redux
- react && redux 学习笔记(二)
- 学习笔记--- S3C2440 对NANDFLASH操作原理与测试代码分析
- 学习笔记6:《大型网站技术架构 核心原理与案例分析》之 万无一失:网站的高可用架构
- 学习笔记 --- LINUX触摸屏原理与驱动分析
- 【JfaceTextFramework学习笔记之四】TextViewer实现原理简单分析
- OpenCV学习笔记(29)KAZE 算法原理与源码分析(三)特征检测与描述
- 逆向工程核心原理学习笔记(十二):分析abex' crackme #1
- 【学习笔记】C语言 随机数的生成原理分析和各类随机数公式
- 编译原理学习笔记12——(魔镜魔镜告诉我—— 构造预测分析程序)——2014_1_28
- 菜鸟学习React Native for Android 之通讯原理分析(JS调用Native)
- 学习笔记2:《大型网站技术架构 核心原理与案例分析》之 大型网站架构演化
- 学习笔记8:《大型网站技术架构 核心原理与案例分析》之 固若金汤:网站的安全架构
- OpenCV学习笔记(28)KAZE 算法原理与源码分析(二)非线性尺度空间构建
- 逆向工程核心原理学习笔记(十三):分析abex' crackme #1 的延伸:将参数压入栈
- React学习笔记_Redux-Saga
- react、redux个人学习笔记