“ 记着当初才来公司的时候上手项目时,对redux的插件使用多少有些困惑,今天来讲解一下react-redux中connect方法第二个参数为对象时的封装。”
1. connect 首先找到connect的封装,在connect.js文件中,先简化一下它的代码封装,得到如下所示代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory }) { return function connect({ mapStateToProps, mapDispatchToProps, // ... }) { // ... return connectHOC(selectorFactory, { // ... }) } } export default createConnect()
有没有瞬间感觉过程清晰了一些呢?其实在我们引用connect时就是引入了connect函数,而当我们调用connect的时候其实它回返了封装之后的组件,用专业术语说的话就是高阶组件,这个就是connect的一个特点。 如果你对connect整体感兴趣,不妨来看看它的源码,在这里我就不一一讲解了。接下来我会来分析connect中第二个参数mapDispatchToProps为对象时的具体流程。
2. mapDispatchToProps为对象时的执行过程 首先来看下面代码,可能你在日常开发中也会遇到或者写过的,但是你有没有想过它为什么会这样来执行呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 export function dispatchTest() { return ()=>{ // todo ... console.log(1111); }; } // 组件中使用 import {connect} from 'react-redux'; // 这里是装饰器的写法,不懂的话可以自行网上搜索查看 @connect(state => ({}), { dispatchTest }) class Test extends Component { componentDidMount () { this.props.dispatchTest(); } }
其实这个时候在componentDidMount生命周期中调用dispatchTest方法是能够打印出1111的,是不是此时你有点疑惑我不是只执行了一次,按理应该返回一个函数呀?不慌,听我慢慢给你道来。 首先去看connect方法中initMapDispatchToProps变量,它是match方法返回值,接着我们又去找match方法,然后我把重要的代码抽出来讲解,它具体代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 /** *@params mapDispatchToProps:connect方法中我们传递的第二个参数值 */ const initMapDispatchToProps = match( mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps' ) 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 }.` ) } }
上面的代码其实也是很简单的,mapDispatchToPropsFactories方法我们来解析一下,在这里我们只分析为对象时的函数源码,具体如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { bindActionCreators } from 'redux' import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps' export function whenMapDispatchToPropsIsObject(mapDispatchToProps) { return mapDispatchToProps && typeof mapDispatchToProps === 'object' ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch) ) : undefined } export default [ whenMapDispatchToPropsIsObject ]
在这里我们重点看处理对象的那个函数,这个函数里首先先来看一下wrapMapToPropsConstant函数的封装,具体代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 export function wrapMapToPropsConstant(getConstant) { return function initConstantSelector(dispatch, options) { const constant = getConstant(dispatch, options) function constantSelector() { return constant } constantSelector.dependsOnOwnProps = false return constantSelector } }
接着再来看看bindActionCreators 函数,它也是connect中很核心的一部分,它的代码封装如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function bindActionCreator(actionCreator, dispatch) { return function () { return dispatch(actionCreator.apply(this, arguments)); }; } // 首先actionCreators的值就是connect中我们传递的第二个参数,它是一个对象 function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch); } if (typeof actionCreators !== 'object' || actionCreators === null) { throw new Error("bindActionCreators expected an object or a function, instead received " + (actionCreators === null ? 'null' : typeof actionCreators) + ". " + "Did you write \"import ActionCreators from\" instead of \"import * as ActionCreators from\"?"); } // 在这里我们应该看下面的代码,为对象时,遍历它的key,如果为函数则返回bindActionCreator处理后的函数 var boundActionCreators = {}; for (var key in actionCreators) { var actionCreator = actionCreators[key]; if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); } } return boundActionCreators; }
然后对于上面的代码,然后将其进行一个整合之后可以得到变量initMapDispatchToProps的值为处理过后的函数,接着我们再去寻找它在哪里执行了,顺着代码找到connectAdvanced.js文件中的initSelector函数,接着在函数中找到selectorFactory函数执行,我们再去看看selectorFactory函数内容执行了什么代码,然后将其简化,只看重点代码,具体如下所示:
1 2 3 4 5 6 7 8 9 export default function finalPropsSelectorFactory(dispatch, { initMapStateToProps, initMapDispatchToProps, initMergeProps, ...options }) { // 其实真正执行函数的是这行代码执行的 const mapDispatchToProps = initMapDispatchToProps(dispatch, options) }
好了,今日的分享就到这里了,讲的不好还请见谅。如果你对connect内部有很浓烈的兴趣那么你可以去看看它内部其他的实现。