前言
redux作为大型应用的状态管理工具,如果想配合react使用,需要借助react-redux。
redux主要完成两件事情:
- 负责应用的状态管理,保证单向数据流
- 当应用状态发生变化,触发监听器。
那么,如果想要将react和redux搭配使用,就需要react组件可以根据redux中所存储的状态(store)更新view。
并且可以改变store。其实react-redux主要就是完成了这两件事情。
第一,通过将store传入root组件的context,使子节点可以获取到state。
第二,通过store.subscribe订阅store的变化,更新组件。
另外还有对于性能的优化,减少不必要的渲染。
react-redux使用
1 | import React from 'react'; |
Provider: 接收从redux而来的store,以供子组件使用。
connect: 高阶组件,当组件需要获取或者想要改变store的时候使用。可以接受四个参数:
- mapStateToProps:取store数据,传递给组件。
- mapDispatchToProps:改变store数据。
- mergeProps:可以在其中对 mapStateToProps, mapDispatchToProps的结果进一步处理
- options:一些配置项,例如 pure.当设置为true时,会避免不必要的渲染
React-Redux的源码目录结构
├── components
│ ├── Provider.js
│ └── connectAdvanced.js
├── connect
│ ├── connect.js
│ ├── mapDispatchToProps.js
│ ├── mapStateToProps.js
│ ├── mergeProps.js
│ ├── selectorFactory.js
│ ├── verifySubselectors.js
│ └── wrapMapToProps.js
├── index.js
└── utils
├── PropTypes.js
├── Subscription.js
├── shallowEqual.js
├── verifyPlainObject.js
├── warning.js
└── wrapActionCreators.js
源码解读
基于react-redux 5.0.6
入口
首先来看一下index.js:
1 | import Provider, { createProvider } from './components/Provider' |
对外提供的API有四个:Provider、createProvider,connectAdvanced,connect
Provider部分
1 | class Provider extends Component { |
Provider组件写了3个方法,getChildContext、constructor、render。目的是使用context向下传递store。将store注入整个React应用的某个入口组件,通常是应用的顶层组件。
constructor是构造方法,this.store = props.store中的this表示当前的组件。在构造函数定义this.store的作用是为了能够在getChildContext方法中读取到store。
getChildContext方法,翻译过来就是上下文。作用是返回store。
render渲染,返回一个react子元素。Children.only是react提供的方法,this.props.children表示的是只有一个root的元素。
Connect部分
1 | export function createConnect({ |
connect是对connectHOC(connectAdvanced)的封装,connectAdvanced使用了defaultSelectorFactory,来创建selector。
react-redux默认的selectorFactory中包含了很多性能优化的部分
connectHOC(connectAdvanced)
connectAdvanced位于src / components / connectAdvanced.js,说明作者把它看做更接近于组件相关,它是Connect的基础。
connectAdvanced的签名如下
1 | (selectorFactory, options) => WrappedComponent => HoistedComponent |
用语言描述就是,connectAdvanced这个方法接受一个selectorFactory方法和一个配置对象,返回一个HOC方法,这个方法接收一个组件参数,并定义了一个叫Connect的组件,然后通过hoist-non-react-statics这个库,把WrappedComponent里面的非React的静态属性方法复制给Connect组件并返回。
selectorFactory部分
在解读selectorFactory代码之前,先说一下options.pure的作用。在开头的例子有一个配置项pure。作用是减少运算优化性能。当设置为false时,react-redux将不会优化,store.subscirbe事件触发,组件就会渲染,即使是没有用到的state更新,也会这样,举个例子
1 | // 大家都知道reducer的写法 |
看下selectorFactory.js finalPropsSelectorFactory作为最终导出的代码
1 | export default function finalPropsSelectorFactory(dispatch, { |
这里很好理解,是对selectorFactory的封装,根据options.pure的值,选取不同的SelectorFactory;
impureFinalPropsSelectorFactory
先来看看简单的impureFinalPropsSelectorFactory,无脑计算返回新值。
1 | export function impureFinalPropsSelectorFactory( |
pureFinalPropsSelectorFactory
在需要的时候才执行mapStateToProps,mapDispatchToProps,mergeProps
1 | export function pureFinalPropsSelectorFactory( |
分两种情况:
- 当第一次运行时,执行 handleFirstCall,将三个map函数运行一遍,并将结果缓存下来
- 之后运行 handleSubsequentCalls,在其中将新的值和缓存的值做比较,如果变化,将重新求值并返回,如果没变化,返回缓存的旧值。
其中 mapFunction.dependsOnOwnProps 代表你传入的mapStateToProps是否使用了ownProps。
如果没有使用,那么props的变化将不会影响结果,换句话说对应的mapFunction将不会执行。
判断方法也很简单,就是获取function形参长度,如何获得呢?
https://juejin.im/post/5b8b5a60e51d4538c411ff12
https://github.com/reduxjs/react-redux/blob/v5.0.6/src/components/Provider.js
https://juejin.im/post/59eb25e651882549fc512bb7