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只要有更新是動態變化都。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章