react-redux 源码解读之connect的mapStateToProps

5 篇文章 0 订阅
5 篇文章 0 订阅

相关文件

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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值