react-redux 源碼解讀之connect的mapDispatchToProps
connect對於mapDispatchToProps的處理跟mapStateToProps的處理流程是一樣的,只有一點點差別,理解mapDispatchToProps時,可以結合mapStateToProps來理解。
以這樣寫法的mapDispatchToProps爲例討論
const faActionCreators = {
abc:aaa=>{
return {
type:'ADD',
id:num++,
text:aaa,
}
},
qqw:aaa=>({
type:'TYY',
id:num++,
text:aaa,
}),
noa:aaa=>({
type:'nono',
id:num++,
text:aaa,
})
}
const mapDispatchToProps = faActionCreators;
在connectAdvanced.js的Connect組件中:
constructor內,定義了
this.initSelector();
從而會執行一次
const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
而且在Connect組件中,只在此時執行一次selectorFactory,以後都不會執行。
selectorFactory()
就是執行
selectorFactory.js 的 finalPropsSelectorFactory()
從而執行一次
const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
initMapDispatchToProps其實就是
wrapMapToProps.js的initConstantSelector;
組件在整個裝載更新銷燬過程當中只會執行一次 selectorFactory;因此只會執行一次finalPropsSelectorFactory(),因此只會執行一次initConstantSelector();
//initConstantSelector方法:
return function initConstantSelector(dispatch, options) {
const constant = getConstant(dispatch, options)
function constantSelector() { return constant }
constantSelector.dependsOnOwnProps = false
return constantSelector
}
getConstant方法爲:
//getConstant方法
dispatch => bindActionCreators(mapDispatchToProps, dispatch)
const mapDispatchToProps
= initMapDispatchToProps(dispatch, options)
這裏的mapDispatchToProps
其實就是函數 constantSelector,從代碼看,此函數不做任何事情,它是一個閉包函數,只是單純從initMapDispatchToProps母函數中取數據,
而constant其實就是包裝好的mapDispatchToProps:
{abc: ƒ, qqw: ƒ, noa: ƒ}
在selectorFactory.js中
dispatchProps = mapDispatchToProps(dispatch, ownProps)
相當於
dispatchProps = constantSelector(dispatch, ownProps)
可以看出dispatch, ownProps這兩個參數是多餘傳遞的。
執行selectorFactory.js中的 dispatchProps = mapDispatchToProps(dispatch, ownProps),並沒有做任何事情,只是單純獲取已經在組件裝載時已經包裝好的mapDispatchToProps。
由於initConstantSelector從始至終只執行一次,所以,對於外層定義的mapDispatchToProps包裝,只進行一次。
initConstantSelector與bindActionCreator
//initConstantSelector方法:
return function initConstantSelector(dispatch, options) {
const constant = getConstant(dispatch, options)
function constantSelector() { return constant }
constantSelector.dependsOnOwnProps = false
return constantSelector
}
從代碼看,initConstantSelector做了兩件事:
1、返回一個constantSelector閉包函數;
2、執行getConstant(dispatch, options)獲得constant,其目的就是存儲好給閉包函數constantSelector使用
getConstant方法爲:
//getConstant方法
dispatch => bindActionCreators(mapDispatchToProps, dispatch)
本文示例中,
actionCreators = faActionCreators;
export default function bindActionCreators(actionCreators, dispatch) {
const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
本文示例中,
actionCreator = aaa=>{
return {
type:'ADD',
id:num++,
text:aaa,
}
};
function bindActionCreator(actionCreator, dispatch) {
return function() {
//很妙的一個用法,通過apply與arguments,父函數可以達到最輕鬆自由靈活的使用子函數actionCreator
// 最終外層發送dispatch時,都會斷點進入到這裏
return dispatch(actionCreator.apply(this, arguments))
}
}
mapDispatchToProps小結
mapDispatchToProps的包裝只會在組件裝載時,在構造函數中包裝一次,以後直至組件銷燬,不會再做包裝的工作。
在在selectorFactory.js中
dispatchProps = mapDispatchToProps(dispatch, ownProps)並沒有做任何事情,只是單純獲取已經在組件裝載時已經包裝好的mapDispatchToProps;
因爲組件更新的時候,會執行this.selector.run(this.props),根據條件不同,可能會執行到 dispatchProps = mapDispatchToProps(dispatch, ownProps),就算這個代碼執行了,也不會再次包裝mapDispatchToProps,而只是簡單的取數據而已。
mapDispatchToProps被包裝後,最終的模樣是什麼,包裝成的模樣其實就是bindActionCreator源碼寫的(上文已經提到),直接給出包裝後最終模樣:
const mapDispatchToProps = (dispatch)=>{
return {
abc:aaa=>{
dispatch({
type:'ADD',
id:num++,
text:aaa,
})
}}
如果好奇,可以在bindActionCreator源碼斷點查看。
mapDispatchToProps 與 mapStateToProps的不同
組件更新,會執行this.selector.run(this.props);
會觸發selectorFactory.js中都會執行
mapDispatchToProps(dispatch, ownProps)
mapStateToProps(state, ownProps)
但是隻有組件裝載過程中,第一次運行this.selector.run,纔會執行同時執行
dispatchProps = mapDispatchToProps(dispatch, ownProps) (handleFirstCall方法)
nextStateProps =mapStateToProps(state, ownProps)
這一次執行獲得的dispatchProps將會被閉包保存,供以後每次this.selector.run後使用;
以後每次執行this.selector.run,基本上可以認爲不再執行dispatchProps = mapDispatchToProps(dispatch, ownProps) ;
而只是從第一次存儲好的閉包的上下文取dispatchProps,
但以後每次執行this.selector.run都會執行nextStateProps =mapStateToProps(state, ownProps),然後更新組件都nextStateProps;
所以組件在運行當中,dispatchProps始終是相同都,nextStateProps只要有更新是動態變化都。