- 阅读react-redux源码 - 零
- 阅读react-redux源码 - 一
- 阅读react-redux源码(二) - createConnect、match函数的实现
上一节看了Provider组件的实现,主要做的事情就是通过Context透传了来自redux的store和监听store变化的事件对象Subscription的实例。
本节会深入到connect组件的内部查看实现方式,整个connect组件的实现相对复杂,并且巧妙。
通过入口文件可以知道connect组件是/src/connect/connect.js的默认导出。文件内容如下:
import connectAdvanced from '../components/connectAdvanced'
import shallowEqual from '../utils/shallowEqual'
import defaultMapDispatchToPropsFactories from './mapDispatchToProps'
import defaultMapStateToPropsFactories from './mapStateToProps'
import defaultMergePropsFactories from './mergeProps'
import defaultSelectorFactory from './selectorFactory'/*connect is a facade over connectAdvanced. It turns its args into a compatibleselectorFactory, which has the signature:(dispatch, options) => (nextState, nextOwnProps) => nextFinalPropsconnect passes its args to connectAdvanced as options, which will in turn pass them toselectorFactory each time a Connect component instance is instantiated or hot reloaded.selectorFactory returns a final props selector from its mapStateToProps,mapStateToPropsFactories, mapDispatchToProps, mapDispatchToPropsFactories, mergeProps,mergePropsFactories, and pure args.The resulting final props selector is called by the Connect component instance wheneverit receives new props or store state.*/
function match(arg, factories, name) {for (let i = factories.length - 1; i >= 0; i--) {const result = factories[i](arg)if (result) return result}return (dispatch, options) => {throw new Error(`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${options.wrappedComponentName}.`)}
}function strictEqual(a, b) {return a === b
}// createConnect with default args builds the 'official' connect behavior. Calling it with
// different options opens up some testing and extensibility scenarios
export function createConnect({connectHOC = connectAdvanced,mapStateToPropsFactories = defaultMapStateToPropsFactories,mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,mergePropsFactories = defaultMergePropsFactories,selectorFactory = defaultSelectorFactory
} = {}) {return function connect(mapStateToProps,mapDispatchToProps,mergeProps,{pure = true,areStatesEqual = strictEqual,areOwnPropsEqual = shallowEqual,areStatePropsEqual = shallowEqual,areMergedPropsEqual = shallowEqual,...extraOptions} = {}) {const initMapStateToProps = match(mapStateToProps,mapStateToPropsFactories,'mapStateToProps')const initMapDispatchToProps = match(mapDispatchToProps,mapDispatchToPropsFactories,'mapDispatchToProps')const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')return connectHOC(selectorFactory, { // used in error messagesmethodName: 'connect',// used to compute Connect's displayName from the wrapped component's displayName.getDisplayName: name => `Connect(${name})`,// if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changesshouldHandleStateChanges: Boolean(mapStateToProps),// passed through to selectorFactoryinitMapStateToProps,initMapDispatchToProps,initMergeProps,pure,areStatesEqual,areOwnPropsEqual,areStatePropsEqual,areMergedPropsEqual,// any extra options args can override defaults of connect or connectAdvanced...extraOptions})}
}export default /*#__PURE__*/ createConnect()
createConnect
首先看看createConnect,关于函数如果有注释先看注释然后主要看函数名,一个好的函数名会表明这个函数是做什么的。再看函数的入参和函数的出参,最后可以看函数的实现方式。
export function createConnect({connectHOC = connectAdvanced,mapStateToPropsFactories = defaultMapStateToPropsFactories,mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,mergePropsFactories = defaultMergePropsFactories,selectorFactory = defaultSelectorFactory
} = {}) {return function connect(mapStateToProps,mapDispatchToProps,mergeProps,{pure = true,areStatesEqual = strictEqual,areOwnPropsEqual = shallowEqual,areStatePropsEqual = shallowEqual,areMergedPropsEqual = shallowEqual,...extraOptions} = {}) {}
}
首先这个函数定义参数的形式有点奇怪,function name({a = 1, b = 2} = {})
这种形式。这其实是JS中的命名参数的写法,和Python、Dart之类的语言中提供的命名参数特性类似。入参的赋值和名字有关,而不是位置。
而入参分别是connectHOC还有剩下来的几个factory,分别是生产mapStateToProps的工厂、生产mapDispathToProps的工厂,生产mergeProps的工厂和生产选择器的工厂。
后面createConnect函数会返回connect函数,而这个connect函数就是连接组件用到的组件。
在底部会执行createConnect
方法则会得到该方法返回的connect函数,在组件中通过connect函数可以连接到顶层Provider提供的store和Subscribtion实例。
connect函数
return function connect(mapStateToProps,mapDispatchToProps,mergeProps,{pure = true,areStatesEqual = strictEqual,areOwnPropsEqual = shallowEqual,areStatePropsEqual = shallowEqual,areMergedPropsEqual = shallowEqual,...extraOptions} = {}) {const initMapStateToProps = match(mapStateToProps,mapStateToPropsFactories,'mapStateToProps')const initMapDispatchToProps = match(mapDispatchToProps,mapDispatchToPropsFactories,'mapDispatchToProps')const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')return connectHOC(selectorFactory, {// used in error messagesmethodName: 'connect',// used to compute Connect's displayName from the wrapped component's displayName.getDisplayName: name => `Connect(${name})`,// if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changesshouldHandleStateChanges: Boolean(mapStateToProps),// passed through to selectorFactoryinitMapStateToProps,initMapDispatchToProps,initMergeProps,pure,areStatesEqual,areOwnPropsEqual,areStatePropsEqual,areMergedPropsEqual,// any extra options args can override defaults of connect or connectAdvanced...extraOptions})}
connect函数就是我们用于连接store的函数。connect函数的入参分别是 mapStateToProps
, mapDispatchToProps
,mergeProps
和一个配置对象。
首先看下入参的配置对象:
{pure = true,areStatesEqual = strictEqual,areOwnPropsEqual = shallowEqual,areStatePropsEqual = shallowEqual,areMergedPropsEqual = shallowEqual,...extraOptions
} = {}
配置对象定义了用于对比数据的函数,分别是对比store中的state,对比被包裹组件自身的props,对比被组件选中的store中的state,对比被包裹组件自身的props和来自store的state被选中的state。
这里要注意,我们知道一个连接到store的组件的props会有两个来源,一个是store,包括store中的state和dispatch。还有一个是父组件传递给组件的props。 而要做到性能最优则是接收到新的值时对这些值分别对比,如果没有改变则不需要更新组件,以优化组件性能。
const initMapStateToProps = match(mapStateToProps,mapStateToPropsFactories,'mapStateToProps'
)
const initMapDispatchToProps = match(mapDispatchToProps,mapDispatchToPropsFactories,'mapDispatchToProps'
)
const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')
这段代码统一处理了三个工厂函数生成目标函数的过程。
function match(arg, factories, name) {for (let i = factories.length - 1; i >= 0; i--) {const result = factories[i](arg)if (result) return result}return (dispatch, options) => {throw new Error(`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${options.wrappedComponentName}.`)}
}
factories是数组,遍历这个数组,找到一个真的返回值,返回出去,否则返回一个会抛出错误的方法。这是责任链模式,factories是一个链,数组的每一项只处理自身的情况,如果匹配则返回,否则返回undefined通知match继续遍历链。
factories对应的数据结构式[() => (fn || undefined), () => (fn || undefined), ...]
。
注意:match从右往左遍历factories数组。
整理好相关入参,全部传递给connectHOC,这个高阶组件是整个connect的核心,如何订阅store更新,如何防止store的更新引起不必要的渲染,如何处理ref的获取,如何使用通过props传入的context和store,如何透传父组件传入的props。这些都会在connect中处理。
- 阅读react-redux源码 - 零
- 阅读react-redux源码 - 一
- 阅读react-redux源码(二) - createConnect、match函数的实现