import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'
export function whenMapStateToPropsIsFunction(mapStateToProps) {
return typeof mapStateToProps === 'function'
? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')
: undefined
}
export function whenMapStateToPropsIsMissing(mapStateToProps) {
return !mapStateToProps ? wrapMapToPropsConstant(() => ({})) : undefined
}
export default [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]
首先mapStateToPropsFactories是一个数组[whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]
。
从名字中很轻易就能看出来数组中两个函数分别处理什么任务。从右往左分别处理了,mapStateToProps参数为假值和mapStateToProps传入的是函数的情况。
whenMapStateToPropsIsMissing:如果入参是假值,那么就返回默认的函数,否则返回undefined。这个undefined被match捕获之后,match会继续遍历factories的下一项,也就是whenMapStateToPropsIsFunction。
whenMapStateToPropsIsFunction:如果mapStateToProps入参是函数,那么就返回wrapMapToPropsFunc(mapStateToProps, ‘mapStateToProps’)的返回值,否则返回undefined。也就是说入参不是函数就会返回undefined。到这里mapStateToPropsFactories数组就结束了,如果最后返回的是undefined,那么match函数会返回一个抛出错误的函数,当执行这个函数的时候回抛出错误。
所以对于入参mapStateToProps来说,如果需要可以传入一个函数,用于筛选组件需要的store.state,否则可不传,但是不能传入非function类型的真值,例如:true,string、number、object之类的值,否则会报错。
import verifyPlainObject from '../utils/verifyPlainObject'
export function wrapMapToPropsConstant(getConstant) {
return function initConstantSelector(dispatch, options) {
const constant = getConstant(dispatch, options)
function constantSelector() {
return constant
}
constantSelector.dependsOnOwnProps = false
return constantSelector
}
}
// dependsOnOwnProps is used by createMapToPropsProxy to determine whether to pass props as args
// to the mapToProps function being wrapped. It is also used by makePurePropsSelector to determine
// whether mapToProps needs to be invoked when props have changed.
//
// A length of one signals that mapToProps does not depend on props from the parent component.
// A length of zero is assumed to mean mapToProps is getting args via arguments or ...args and
// therefore not reporting its length accurately..
// function mapStateToProps(state, ownProps) { }
export function getDependsOnOwnProps(mapToProps) {
return mapToProps.dependsOnOwnProps !== null &&
mapToProps.dependsOnOwnProps !== undefined
? Boolean(mapToProps.dependsOnOwnProps)
: mapToProps.length !== 1
}
// Used by whenMapStateToPropsIsFunction and whenMapDispatchToPropsIsFunction,
// this function wraps mapToProps in a proxy function which does several things:
//
// * Detects whether the mapToProps function being called depends on props, which
// is used by selectorFactory to decide if it should reinvoke on props changes.
//
// * On first call, handles mapToProps if returns another function, and treats that
// new function as the true mapToProps for subsequent calls.
//
// * On first call, verifies the first result is a plain object, in order to warn
// the developer that their mapToProps function is not returning a valid result.
//
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)
}
// allow detectFactoryAndVerify to get ownProps
proxy.dependsOnOwnProps = true
proxy.mapToProps = function detectFactoryAndVerify(
stateOrDispatch,
ownProps
) {
proxy.mapToProps = mapToProps
proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
let props = proxy(stateOrDispatch, ownProps)
// function mapToStateWrap() { return function mapToState(state, ownProps) {} }
if (typeof props === 'function') {
proxy.mapToProps = props
proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
props = proxy(stateOrDispatch, ownProps)
}
if (process.env.NODE_ENV !== 'production')
verifyPlainObject(props, displayName, methodName)
return props
}
return proxy
}
}
这两个包裹函数的目的则是为mapToProps函数增强一些功能,mapToPrps函数包括mapStateToProps和mapDispatchToProps。
wrapMapToPropsConstant:该函数会用一个返回常量的函数作为入参,并返回一个函数。被返回的函数的返回值也是一个函数,也就是说一共有三层,需要执行三次才能拿到结果,也就是入参函数返回的常量。
wrapMapToPropsFunc:这个函数比较复杂。它返回一个mapToProps的代理函数,代理函数增加了几个功能点:
首先定义一个proxy函数:
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
return proxy.dependsOnOwnProps
? proxy.mapToProps(stateOrDispatch, ownProps)
: proxy.mapToProps(stateOrDispatch)
}
这个函数会检查是否依赖组件原来的props如果依赖则传入,否则不传入。
默认mapToProps函数依赖组件被传入的props:
proxy.dependsOnOwnProps = true
这一段比较烧脑了,定义了proxy函数中首次真正执行的proxy.mapToProps函数:
proxy.mapToProps = function detectFactoryAndVerify(
stateOrDispatch,
ownProps
) {
proxy.mapToProps = mapToProps
proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
let props = proxy(stateOrDispatch, ownProps)
// function mapToStateWrap() { return function mapToState(state, ownProps) {} }
if (typeof props === 'function') {
proxy.mapToProps = props
proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
props = proxy(stateOrDispatch, ownProps)
}
if (process.env.NODE_ENV !== 'production')
verifyPlainObject(props, displayName, methodName)
return props
}
首先这个函数是stateOrDispatch,而入参是stateOrDispatch和需要连接store的组件自身的props。
将mapToProps赋值给proxy.mapToProps,然后检查mapToProps是否依赖ownProps。然后执行 :
proxy.dependsOnOwnProps
? proxy.mapToProps(stateOrDispatch, ownProps)
: proxy.mapToProps(stateOrDispatch)
上两行代码已经将proxy.dependsOnOwnProps和proxy.mapToProps换掉了所以不会形成死循环再次进入当前方法proxy.mapToProp方法。
获取到mapToProps的返回值,检查返回值,如果是function那么这个代理函数就会将这个props当做真正的mapToProps函数。然后再赋值并且执行一遍。
if (typeof props === 'function') {
proxy.mapToProps = props
proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
props = proxy(stateOrDispatch, ownProps)
}
再接下来则会判断新得到的props是否是简单对象。如果不是简单对象并且不在生产环境中就要报错,否则直接返回props。
小结:wrapMapToPropsFunc
函数将将mapStateToProps和mapDispatchToProps归为一类,都是输入一个值,输出一个对象,过程都一样,所以具体代理的是mapDispatch还是mapState只和入参有关,代理的过程是一样的。
export function getDependsOnOwnProps(mapToProps) {
return mapToProps.dependsOnOwnProps !== null &&
mapToProps.dependsOnOwnProps !== undefined
? Boolean(mapToProps.dependsOnOwnProps)
: mapToProps.length !== 1
}
这个函数相对简单,如果mapToProps有显示声明是否依赖ownProps如果有将其转换为boolean返回,否则使用隐式判断记mapToProps的入参有几个,如果不是1个(其实这里应该是大于1吧?)则认为入参至少是两个,而第二个参数就希望是ownProps。
这个方法主要验证对象是否是简单对象,具体说只做存储数据用,具体为什么要求mapToProps函数返回的是简单对象,就并没有头绪了。 判断是否是简单对象的方法如下:
export default function isPlainObject(obj) {
if (typeof obj !== 'object' || obj === null) return false
let proto = Object.getPrototypeOf(obj)
if (proto === null) return true
let baseProto = proto
while (Object.getPrototypeOf(baseProto) !== null) {
baseProto = Object.getPrototypeOf(baseProto)
}
return proto === baseProto
}
如果入参不是对象则不是简单对象,因为连对象都不是。
获取对象原型,如果对象原型是null(Object.create(null))则是简单对象。
遍历原型链,找到原型上倒数第二个节点,与proto对于,如果相等则表示obj是简单对象,否则不是简单对象。
class A extends B {} A的实例就不是简单对象,因为A的原型是B.prototype而不是Object.prototype。
未解决的问题:为什么要遍历而不是检查两层原型链?如:
export default function isPlainObject(obj) {
if (typeof obj !== 'object' || obj === null) return false
let proto = Object.getPrototypeOf(obj)
if (proto === null) return true
return Object.getPrototypeOf(proto) === null
}
import { bindActionCreators } from 'redux'
import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'
export function whenMapDispatchToPropsIsFunction(mapDispatchToProps) {
return typeof mapDispatchToProps === 'function'
? wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps')
: undefined
}
export function whenMapDispatchToPropsIsMissing(mapDispatchToProps) {
return !mapDispatchToProps
? wrapMapToPropsConstant(dispatch => ({ dispatch }))
: undefined
}
export function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
return mapDispatchToProps && typeof mapDispatchToProps === 'object'
? wrapMapToPropsConstant(dispatch =>
bindActionCreators(mapDispatchToProps, dispatch)
)
: undefined
}
export default [
whenMapDispatchToPropsIsFunction,
whenMapDispatchToPropsIsMissing,
whenMapDispatchToPropsIsObject
]
Factories从右往左mapDispatchToProps分别是object的情况,没有传的情况和是函数的情况。
其中使用的到的wrapMapToPropsConstant
和 wrapMapToPropsFunc
在上面有介绍,而bindActionCreators
的入参希望是一个对象,对象对应的值是actionCreator,执行的结果则是将actionCreator用函数包起来,例如这样:
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
import verifyPlainObject from '../utils/verifyPlainObject'
export function defaultMergeProps(stateProps, dispatchProps, ownProps) {
return { ...ownProps, ...stateProps, ...dispatchProps }
}
export function wrapMergePropsFunc(mergeProps) {
return function initMergePropsProxy(
dispatch,
{ displayName, pure, areMergedPropsEqual }
) {
let hasRunOnce = false
let mergedProps
return function mergePropsProxy(stateProps, dispatchProps, ownProps) {
const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps)
if (hasRunOnce) {
if (!pure || !areMergedPropsEqual(nextMergedProps, mergedProps))
mergedProps = nextMergedProps
} else {
hasRunOnce = true
mergedProps = nextMergedProps
if (process.env.NODE_ENV !== 'production')
verifyPlainObject(mergedProps, displayName, 'mergeProps')
}
return mergedProps
}
}
}
export function whenMergePropsIsFunction(mergeProps) {
return typeof mergeProps === 'function'
? wrapMergePropsFunc(mergeProps)
: undefined
}
export function whenMergePropsIsOmitted(mergeProps) {
return !mergeProps ? () => defaultMergeProps : undefined
}
export default [whenMergePropsIsFunction, whenMergePropsIsOmitted]
和上面两个factories一样的模式,主要看下函数wrapMergePropsFunc
主要做的事情是缓存mergedProps,如果对比出
nextMergedProps
计算出来的props和缓存的mergedProps一样,那么就直接返回缓存的mergedProps。其中最终的props主要有三个来源,一个是stateProps,一个是dispatchProps还有一个是ownProps。