react-redux 源码解读之connect的mapStateToProps
相关文件
react-redux/src/connect/connect.js --入口文件:接受传入的mapStateToProps
react-redux/src/connect/mapStateToProps.js --对传入mapStateToProps进行空值、或function判断处理
react-redux/src/connect/wrapMapToProps.js --包装mapStateToProps和mapDispitchToProps
react-redux/src/components/connectAdvanced.js -- 不做处理,转发一下
react-redux/src/connect/selectorFactory.js --调用mapStateToProps的地方
下面对以上相关文件中,关于mapStateToProps部分进行讲解。
mapStateToProps从定义到最终调用的过程
我们在各个组件中使用connect,并定义mapStateToProps。
connect.js
connect(mapStateToProps,…) --接受各组件定义的mapStateToProps:
并包装mapStateToProps,得到initMapStateToProps,并传给 connectAdvanced.js:
//connect.js
const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps') ---包装mapStateToProps
connectHOC(..., { initMapStateToProps,.... })
connectAdvanced.js
connectOptions包含initMapStateToProps.
connectAdvanced.js接收connectOptions (initMapStateToProps),不做任何改变,直接转发,传给selectorFactory.js :
//connectAdvanced.js
connectAdvanced(... {...connectOptions } = {}) ---connectOptions包含initMapStateToProps;
const selectorFactoryOptions = { ...connectOptions,...}
selectorFactory(... selectorFactoryOptions)
selectorFactory.js
selectorFactory.js接收initMapStateToProps,并执行一次它,得到最终每次调用的mapStateToProps
//selectorFactory.js
const mapStateToProps = initMapStateToProps(dispatch, options)
//以后每次都在handleNewState方法中调用
mapStateToProps(state, ownProps)
以上过程我们可以看到,其实都是针对initMapStateToProps进行,中间有些过程只是直接转发,并没有操作改变mapStateToProps。
简单点,可以将上一节过程简化为:
//这里的mapStateToProps 为 各组件定义的mapStateToProps
const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
//这里的mapStateToProps 为 各组件定义的mapStateToProps经过connect框架转化后最终执行的mapStateToProps
const mapStateToProps = initMapStateToProps(dispatch, options)
connect框架为了达到使用方便,允许用户灵活定义mapStateToProps,框架会对用户定义的mapStateToProps经过一系列转化,变成可用的mapStateToProps。
读懂initMapStateToProps才是理解mapStateToProps的关键。
initMapStateToProps
为方便理解,我们只讨论组件内定义的mapStateToProps 非空,且为function的情况
//connect.js
const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
....
function match(arg, factories, name) {
for (let i = factories.length - 1; i >= 0; i--) {
const result = factories[i](arg)
if (result){
return result
}
}
}
mapStateToPropsFactories是一个这样的数组:
//mapStateToProps.js
export function whenMapStateToPropsIsFunction(mapStateToProps) {
return (typeof mapStateToProps === 'function')
? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')
: undefined
}
export function whenMapStateToPropsIsMissing(mapStateToProps) {
return (!mapStateToProps)
? wrapMapToPropsConstant(() => ({}))
: undefined
}
export default [
whenMapStateToPropsIsFunction,
whenMapStateToPropsIsMissing
]
以上结合match使用,妙的地方在于 :
match是使用length–进行for遍历,whenMapStateToPropsIsMissing是第一个被match处理的。
whenMapStateToPropsIsMissing只处理mapStateToProps为空情况,当mapStateToProps有值的时候,返回一个undefined,
从而通过match让whenMapStateToPropsIsFunction来处理,
match函数通过return终止操作
于是
const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, ‘mapStateToProps’)
相当于
const initMapStateToProps = wrapMapToPropsFunc(mapStateToProps, ‘mapStateToProps’);
wrapMapToPropsFunc方法为:
//wrapMapToProps.js
export function wrapMapToPropsFunc(mapToProps, methodName) {
return function initProxySelector(dispatch, { displayName }) {
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
return proxy.dependsOnOwnProps
? proxy.mapToProps(stateOrDispatch, ownProps)
: proxy.mapToProps(stateOrDispatch)
}
return proxy
}
}
由此可见
wrapMapToPropsFunc(mapStateToProps, ‘mapStateToProps’)
相当于
initProxySelector方法。
因此可以认为:
const initMapStateToProps = initProxySelector;
上文说到而我们最终使用的mapStateToProps:
const mapStateToProps = initMapStateToProps(dispatch, options);
因此
最终的mapStateToProps函数其实就是initProxySelector()执行后得到的结果,我们可以认为以下是相等的:
//最终使用到的mapStateToProps
const mapStateToProps = proxy;
由此,我们看到,在各个组件中,在connect中定义的mapStateToProps其实将转化为代理方法proxy。
以上过程我们简化为:
const initMapStateToProps = wrapMapToPropsFunc(mapStateToProps, ‘mapStateToProps’);
const mapStateToProps = initMapStateToProps()=proxy;
所以整个过程都基于高阶函数wrapMapToPropsFunc进行。
wrapMapToPropsFunc
export function wrapMapToPropsFunc(mapToProps, methodName) {
return function initProxySelector(dispatch, { displayName }) {
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
return proxy.dependsOnOwnProps
? proxy.mapToProps(stateOrDispatch, ownProps)
: proxy.mapToProps(stateOrDispatch)
}
proxy.dependsOnOwnProps = true
proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
proxy.mapToProps = mapToProps
proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
let props = proxy(stateOrDispatch, ownProps)
return props
}
return proxy
}
}
wrapMapToPropsFunc的多层闭包函数设计方式
wrapMapToPropsFunc这个函数,其实是一个嵌套了三层的高阶闭包函数,
函数返回一个函数,返回的函数又返回一个函数,
也就是说
const proxy = wrapMapToPropsFunc(mapToProps, methodName)(dispatch, { displayName })
proxy是最终调用的mapStateToProps;
为什么使用多层闭包函数设计方式,这里的原因主要在于传值,下一次return出来的闭包函数,执行时能够始终拿到对应母函数传过来的值。
比如:
initProxySelector = wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps');
闭包函数initProxySelector每次执行的时候就可以使用mapStateToProps, ‘mapStateToProps’;
proxy = initProxySelector(dispatch, { displayName }) ;
闭包函数proxy执行时,就可以始终使用dispatch, { displayName };
所以多层闭包的使用,最主要还是为了能够多一次传值 给最终返回闭包的函数 使用。
多层闭包函数设计方式也是一个很妙的js设计方法。
wrapMapToPropsFunc分析
明白了多层闭包设计的目的,我们针对wrapMapToPropsFunc多层执行的用意:
const proxy = wrapMapToPropsFunc(mapToProps, methodName)(dispatch, { displayName })
wrapMapToPropsFunc(mapToProps, methodName)
它就是为了给后面返回的闭包函数提供参数数据mapToProps, methodName;
这里的mapToProps就是组件内定义的mapStateToProps;
initProxySelector(dispatch, { displayName })
initProxySelector:
//wrapMapToProps.js
return function initProxySelector(dispatch, { displayName }) {
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
}
proxy.dependsOnOwnProps = true
proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
}
return proxy
}
initProxySelector(dispatch, { displayName })做了三件事情:
1、提供参数变量 dispatch, { displayName })供以后的闭包函数使用;
2、设置proxy.dependsOnOwnProps = true
3、定义proxy
proxy
最后再看重头戏:
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
return proxy.dependsOnOwnProps
? proxy.mapToProps(stateOrDispatch, ownProps)
: proxy.mapToProps(stateOrDispatch)
}
proxy.dependsOnOwnProps = true
proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
proxy.mapToProps = mapToProps
//getDependsOnOwnProps判断mapToProps方法有几个形参,如果为一个,就为false,如果有两个形参为true;
proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
let props = proxy(stateOrDispatch, ownProps)
return props
}
因为 proxy.mapToProps = mapToProps这样赋值过后,在mapToPropsProxy(stateOrDispatch, ownProps)执行第一次后,
以后每次执行,代码等同与:
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
return mapToProps(stateOrDispatch, ownProps)
}
也因此 proxy 完全等同于 函数mapToProps;
而mapToProps就是组件中定义的mapStateToProps
//组件中定义的mapStateToProps
proxy === mapStateToProps
proxy这样的设计有几点用意:
1、通过传入的stateOrDispatch形参的个数,设置proxy.dependsOnOwnProps;
控制是否传入ownProps参数。
2、设计灵活,根据不同的判断调用不同的函数或传入不同的参数,后期还可以加需求
3、代码更直观,美观
经过上面绕了一大圈一大圈,其实你发现 上面四个js什么都没做,
将mapStateToProps传进去后,又原封不动返回出来了mapStateToProps;
其实这种模式下,能够包装mapStateToProps,让用户使用更加简洁定义mapStateToProps,然后框架层面进行包装。
对于proxy的理解举例
function fn(a,b) {
console.log(a)
console.log(b)
return {...a}
}
function wrapMapToPropsFunc(mapToProps) {
const proxy = function mapToPropsProxy(stateOrDispatch) {
return proxy.dependsOnOwnProps
? proxy.mapToProps(stateOrDispatch,5)
: proxy.mapToProps(stateOrDispatch)
}
proxy.dependsOnOwnProps = true
proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch) {
console.log(1)
proxy.mapToProps = mapToProps
//proxy.mapToProps重新定义为mapToProps也就是fn,以后再次执行将只执行fn;
//不会再执行detectFactoryAndVerify,这种设计模式下,detectFactoryAndVerify只会执行一遍,
// 后期每次执行都只是执行fn
//所以这里都abc完全等于fn
proxy.dependsOnOwnProps = false
//由于fn是一个返回对象都函数,所以这里需要执行 本身 返回一次对象
let props = proxy(stateOrDispatch)
return props
}
return proxy
}
var abc = wrapMapToPropsFunc(fn);
abc();//执行detectFactoryAndVerify
abc();//不再执行detectFactoryAndVerify
// 所以这里都abc完全等于fn,proxy也安全等于fn
// 以上设计,会得到如下结果:
// todo abc=proxy=fn
proxy进阶分析
mapStateToProps可以定义为如下形式,当为此种模式时每次执行mapStateToProps可以获得初始时的initstate,
const mapStateToPropsCreator = (state)=>{
return ({addRedux:state.addRedux})
};
const mapStateToProps = (initstate,ownprops)=>{
return mapStateToPropsCreator;
};
这都是在proxy上定义:
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
....
proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
....
if (typeof props === 'function') {
proxy.mapToProps = props
proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
props = proxy(stateOrDispatch, ownProps)
}
....
}
return proxy