摘要:返回值決定了在更新的時候,是否要調用方法進行更新這里通過判斷方法的來進行判斷是否需要,當的時候,就是需要。同時,一個影對象會被造出并合并到組件的中。在這種情況下,返回的會被作為真正的。
注:這篇文章只是講解React Redux這一層,并不包含Redux部分。Redux有計劃去學習,等以后學習了Redux源碼以后再做分析;
注:代碼基于現在 (2016.12.29) React Redux 的最新版本 (5.0.1)。
這里講的其實是connect方法的基礎,本來是準備先把connectAdvanced和Provider寫完,再專門寫connect和相關方法的,但是發現connectAdvanced也用到了很多這些基礎方法,所以就停下來,寫下面的這些東西。
之前沒有上過代碼結構圖,這里貼張圖上來(5.0.1版本)
React Redux在最近的幾個版本中對代碼做了拆分和優化,之前看4.x的代碼的時候,connect下面所有的方法都在一個文件里面,而且邏輯也不夠清晰。當時本來想吐槽他們的源代碼的,但是升級到5.x以后,就發現代碼清晰很多,只不過代碼邏輯復雜度更上一層樓。。。
dependsOnOwnProps多帶帶提出來這個屬性,做一個簡單的說明。dependsOnOwnProps屬性并不是對外的屬性,而是代碼內部邏輯使用的,會在多個方法中用到,這個屬性主要是針對connect的兩個參數mapStateToProps, mapDispatchToProps。
根據文檔,mapStateToProps可以是function或者null, mapDispatchToProps可以是function, object或者null。如果是function,當定義的時候,可以選擇是否傳入ownProps對象,比如function mapStateToProps(state, ownProps) {},這就說明這個function的返回結果可能是基于ownProps的,所以每次ownProps發生改變的時候,都需要調用這個方法進行更新。
所以dependsOnOwnProps就是當ownProps更新的時候,用來判斷是否需要重新調用對應方法獲取新的結果。
wrapMapToProps.js getDependsOnownProps這段代碼主要的作用就是判斷map(State/Dispptch)ToProps方法是否需要ownProps。
返回值決定了在props更新的時候,是否要調用map(State/Dispptch)ToProps方法進行更新
export function getDependsOnOwnProps(mapToProps) { return (mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined) ? Boolean(mapToProps.dependsOnOwnProps) : mapToProps.length !== 1 }
這里通過判斷方法的length來進行判斷是否需要ownProps,當mapToProps.length !== 1的時候,就是需要。反之,就是不需要。
這里有幾種情況:
function mapStateToProps(state){},那么mapStateToProps.length === 1,不需要ownProps
function mapStateToProps(state, ownProps){}, 那么mapStateToProps.length === 2, 需要ownProps
function mapStateToProps(){ arguments[0]; } 或者 function mapStateToProps(...args){}, 那么mapStateToProps.length === 0, 由于無法通過定義判斷是否需要ownProps,所以默認是需要
如果之前已經設置過了,那么就不需要設置了,重用Boolean(mapToProps.dependsOnOwnProps)
wrapMapToPropsConstant首先,根據文檔mapDispatchToProps是optional的,而且可以是function或者object。
這塊代碼主要針對mapDispatchToProps是object或者null的情況,把這個對象進行包裝,生成(dispatch, options)=>()=>dispatchedActions的方法,并添加dependsOnOwnProps屬性。
export function wrapMapToPropsConstant(getConstant) { return function initConstantSelector(dispatch, options) { const constant = getConstant(dispatch, options) function constantSelector() { return constant } constantSelector.dependsOnOwnProps = false return constantSelector } }
首先查看一下這個方法的調用,在mapDispatchToProps.js里面有兩處調用:
export function whenMapDispatchToPropsIsFunction(mapDispatchToProps) {...} export function whenMapDispatchToPropsIsMissing(mapDispatchToProps) { return (!mapDispatchToProps) ? wrapMapToPropsConstant(dispatch => ({ dispatch })) // 會生成(dispatch, options)=>()=>({dispatch}) : undefined } export function whenMapDispatchToPropsIsObject(mapDispatchToProps) { return (mapDispatchToProps && typeof mapDispatchToProps === "object") ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch)) // 會生成(dispatch, options)=>()=>bindActionCreators(mapDispatchToProps, dispatch),也就是綁定過dispatch的actions : undefined }
在這里,如果mapDispatchToProps為null或者是object的時候,調用wrapMapToPropsConstant方法。
而在Object里面,由于調用了bindActionCreators方法,所以最后生成的都是綁定過dispatch的actions,這也就是為什么在connect的組件中,直接調用this.props.action()就可以通知redux,而不是dispatch(this.props.action()).
文檔中也提到:
如果傳入的時object,那么里面所有的function都會被認為是一個Redux的action creator。同時,一個影對象會被造出并合并到組件的props中。這個影對象會包含mapDispatchToProps中同樣的function名,但是每一個action creator都被dispatch包裹,所以就可以直接調用。
注: 在whenMapDispatchToPropsIsObject只是返回(dispatch, options)=>()=>disptachedActions,然后會在selectorFactory里面傳入dispatch和options,并根據dependsOnOwnProps調用,最后獲得里面的constant。最后會在mergeProps方法里面,把state, constant, props進行合并
wrapMapToPropsFunc這個方法主要是針對mapStateToProps和mapDispatchToProps是function的情況。
mapStateToProps
如果傳入了這個function,那么組件就會監聽Redux store的更新。一旦store發生更新,那么mapStateToProps就會被調用。返回的結果必須是一個plain object,結果會被合并到組件的props中。如果沒有傳入這個對象,那么組件就不會監聽Redux Store。如果ownProps在定義中作為一個參數,那么ownProps的值就是傳入組件的props,同時每次props發生改變,mapStateToProps就會被重新調用。(如果組件接受的props發生了淺層改變(shallowly changed),同時ownProps也作為一個參數被傳入,那么mapStateToProps就會被重新調用)
mapDispatchToProps
如果傳入的是function,那么dispatch會被傳入到這個function中。你可以按照自己的意愿返回被dispatch綁定過的action creators.
兩個都有
Note: 在一切特殊情況下,你可能需要對渲染性能有更多的掌控,mapDispatchToProps()和mapStateToProps()也可以返回一個function。在這種情況下,返回的function會被作為真正的mapDispatchToProps(mapStateToProps)。這么做,允許你可以做一些記憶類的操作(應該是說,可以記錄上一次的state和ownProps,在function里面可以做對比,減少不必要的改變)
代碼是這樣子的:
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) } proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps) // 初始化時根據mapToProps來判斷是否需要ownProps proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) { // 第一次調用的時候,會進到這里 proxy.mapToProps = mapToProps let props = proxy(stateOrDispatch, ownProps) // 先獲取mapToProps的返回值 if (typeof props === "function") { // 如果返回值是function,那么符合文檔中說的特殊情況 proxy.mapToProps = props // 把這個props當作真正的mapToProps proxy.dependsOnOwnProps = getDependsOnOwnProps(props) // 根據新的props方法來更新是否需要ownProps props = proxy(stateOrDispatch, ownProps) // 獲取最終結果 } if (process.env.NODE_ENV !== "production") verifyPlainObject(props, displayName, methodName) // 如果是非production環境下,判斷結果的類型 return props } return proxy } }
這里的mapToProps就是map(State/Dispatch)ToProps的意思,這個代碼主要做了幾個工作:
判斷這個方法是否基于ownProps
第一次調用的時候(或者說connect方法初始化的時候),如果mapToProps返回的時一個function,那么就把這個function當作真正的mapToProps
第一次調用的時候,檢查返回的對象是不是一個plain object, 如果不是,那么就在非production環境下拋出一個異常,提示開發者
注: 從代碼里面看出一點,就是當function返回function的情況,其實dependsOnOwnProps并不是根據外層的function來定的,而是根據返回的function而定的。而且,像文檔中所說,他的主要作用是做對比(和上一個state, ownProps),所以我猜代碼應該像這個樣子
function mapStateToProps(state, ownProps){ // ownProps可選 let oldState = state, oldProps = ownProps, lastState; return function(state, ownProps){ // ownProps可選 // 在這里對比當前state, ownProps和之前的oldState, oldProps,來生成新的state,或者直接用之前的state let ret = {}; if(!lastState) { lastState = state; // do some computation here. } else if(!shallowEqual(state, oldState) || !shallowEqual(oldProps, ownProps)) { lastState = state; // do some computation here } oldState = state; oldProps = ownProps; return lastState; } }
同時,真正是否渲染根據ownProps改變,是基于內層的function來定的。所以說:
dependsOnOwnProps為false
function mapStateToProps(state, ownProps){ return function(state){ } }
dependsOnOwnProps為true
function mapStateToProps(state){ return function(state, ownProps){ } }mapDispatchToProps.js
這個JS中提供了三個方法,分別應對mapDispatchToProps是function,object和null的情況。
whenMapDispatchToPropsIsFunctionexport function whenMapDispatchToPropsIsFunction(mapDispatchToProps) { return (typeof mapDispatchToProps === "function") ? wrapMapToPropsFunc(mapDispatchToProps, "mapDispatchToProps") : undefined }
當mapDispatchToProps是function的時候,用wrapMapToPropsFunc來進行調用。最后返回的應該是(dispatch, {displayName})=>(dispatch, ownProps)=>binded Action Creators
whenMapDispatchToPropsIsMissingexport function whenMapDispatchToPropsIsMissing(mapDispatchToProps) { return (!mapDispatchToProps) ? wrapMapToPropsConstant(dispatch => ({ dispatch })) : undefined }
當mapDispatchToProps是null的時候,調用wrapMapToPropsConstant(dispatch => ({ dispatch })),這里這么做的目的是,只把dispatch綁定到props上面。這里返回的是(dispatch, options)=>()=>{ dispatch }
whenMapDispatchToPropsIsObjectexport function whenMapDispatchToPropsIsObject(mapDispatchToProps) { return (mapDispatchToProps && typeof mapDispatchToProps === "object") ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch)) : undefined }
當mapDispatchToProps是object的時候,調用wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch))進行包裝,根據之前對wrapMapToPropsConstant的介紹,這里返回的是(dispatch, options)=>()=>binded Action Creators
最后的export defaultexport default [ whenMapDispatchToPropsIsFunction, whenMapDispatchToPropsIsMissing, whenMapDispatchToPropsIsObject ]
這里寫的很有趣,也很值得借鑒。先看一下調用的地方:
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是之前的數組。由于這三個方法如果類型判斷不正確,就會返回undefined,所以這里的match通過循環的方法來進行判斷和解析。如果result存在,那么就說明判斷通過,就返回。如果result不存在,那么就檢查下一個。
如果按照平時的寫法,我肯定會用很多的if來進行判斷,這樣子反而增加了代碼量,而且不美觀。比如這樣:
export default function(mapDispatchToProps){ let result = undefined; result = whenMapDispatchToPropsIsFunction(mapDispatchToProps); if(!result) result = whenMapDispatchToPropsIsMissing(mapDispatchToProps); if(!result) result = whenMapDispatchToPropsIsObject(mapDispatchToProps); return result; }
和源碼里的比較,感覺還是源碼里寫的好看,有趣。
mapStateToProps.js這個JS中提供了兩個方法,分別應對mapDispatchToProps是function和null的情況。
whenMapStateToPropsIsFunctionexport function whenMapStateToPropsIsFunction(mapStateToProps) { return (typeof mapStateToProps === "function") ? wrapMapToPropsFunc(mapStateToProps, "mapStateToProps") : undefined }
當mapStateToProps是function的時候,進行封裝。這里返回的結果是(dispatch, {displayName})=>(state, ownProps)=>state result
whenMapStateToPropsIsMissingexport function whenMapStateToPropsIsMissing(mapStateToProps) { return (!mapStateToProps) ? wrapMapToPropsConstant(() => ({})) : undefined }
這里,當mapStateToProps是null的時候,返回結果是(dispatch, {displayName})=>(state, ownProps)=>{}。之所以這里返回的是{},而不是undefined后者null,是因為便于以后在mergeProps的時候不需要再去檢查undefined和null的情況。
export defaultexport default [ whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing ]mergeProps.js
這個JS中提供了兩個方法,分別應對mergeProps是function和null的情況。
插一段mergeProps的文檔:
defaultMergeProps如果這個值不是undefined,那么它會接受mapStateToProps(), mapDispatchToProps()和父組件傳入的props。這個方法返回的plain object會被當作組件的props傳給組件。你可以在這個方法里面只選擇部分props返回,或者根據傳入的props給action creators綁定一些參數。如果沒有傳這個值,默認值是Object.assign({}, ownProps, stateProps, dispatchProps)
export function defaultMergeProps(stateProps, dispatchProps, ownProps) { return { ...ownProps, ...stateProps, ...dispatchProps } }
默認的mergeProps方法。和文檔中說的一樣,不用解釋。
wrapMergePropsFuncexport function wrapMergePropsFunc(mergeProps) { return function initMergePropsProxy( dispatch, { displayName, pure, areMergedPropsEqual } // 后面的其實都是option里面東西 ) { let hasRunOnce = false // 第一次不用檢查,直接賦值,所以有一個flag let mergedProps // 記錄props,用于賦值和對比 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 } } }
在這里先說一下pure這個option
pure是connect的第四個參數option中的一項,默認值是true。在這里的定義是,一個組件是pure的話,就說明這個組件并不依賴于除了props和Redux store的state意外的任何輸入和狀態。如果pure是true,當相關的props和state經過對比,并沒有發生改變的話,那么就不去調用mapStateToProps,mapDispatchToProps和mergeProps。反之,無論是否改變,都會調用這些方法更新props。(注: 這里的經過對比,在這里方法里是areMergedPropsEqual這個對比的方法。另外還有areStatesEqual, areOwnPropsEqual, areStatePropsEqual都是在option中定義)
這里做了幾件事情:
第一次調用的時候,不進行是否改變的判斷,同時檢查返回值的格式
第二次以后的調用,都會進行pure和改變的判斷,如果改變了,才修改mergedProps值,減少了不必要的渲染
export defaultexport function whenMergePropsIsFunction(mergeProps) { return (typeof mergeProps === "function") ? wrapMergePropsFunc(mergeProps) : undefined } export function whenMergePropsIsOmitted(mergeProps) { return (!mergeProps) ? () => defaultMergeProps : undefined } export default [ whenMergePropsIsFunction, whenMergePropsIsOmitted ]
和之前的幾個js一樣,包含isFunction和isOmitted兩個方法。
一點總結可以利用function.length來判斷這個function在定義的時候有幾個參數??梢杂脕砼袛嗄承﹨凳欠裥枰獋魅?,或許可以減少不必要的計算。但是,需要注意的是,當function中使用arguments,或者function(...args){}的時候,雖然內部可能會使用多個參數,但是length返回0,無法通過length屬性進行判斷。
由于stateProps和dispatchProps是否根據ownProps來進行更新是根據function.length來定的,所以如果不需要,就不要在定義的時候加上這個參數。
需要處理一個對象,這個對象可能有多種類型的時候,我們可以不選擇在方法寫if..else來判斷類型,可以像mapDispatchToProps, mapStateToProps一樣,返回一個function的數組,每個function里面判斷是否是符合的類型,是的話,按這個類型處理。不是的話,返回undefined。簡單,好用。
當一個系統接受的同一個參數可能是多種不同的類型,該怎么辦?我們并不期望在里面使用過多的typeof, if...else來進行判斷,代碼會顯得比較啰嗦臃腫。我們可以考慮把所有類型都轉化成為統一的一個類型。就像這里的wrapMapToProps一樣,把Object,function, null都轉成function,以后就不需要多余處理類型了
下篇:《Connect工具類篇(2)》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88076.html
摘要:負責記錄所有的狀態,以便在部分狀態發生改變,而不影響組件渲染的時候,可以避免不必要的渲染。作用是,每次或發生改變以后,調用這個返回的,獲取更新后的最終。由于發生改變,并不一定會造成返回結果的改變,所以需要根據檢查后的結果來判定是否調用方法。 注:這篇文章只是講解React Redux這一層,并不包含Redux部分。Redux有計劃去學習,等以后學習了Redux源碼以后再做分析注:代碼基...
摘要:的作用在文檔中是這么說的給下級組件中的提供可用的的對象。這個文件里的主要是被方法引入,并傳給的,算是一個默認的。表示當前的名稱。這個值表示在里面的值。便于控制,同時某些不需要渲染的,也不會造成渲染。 注:這篇文章只是講解React Redux這一層,并不包含Redux部分。Redux有計劃去學習,等以后學習了Redux源碼以后再做分析注:代碼基于現在(2016.12.29)React ...
摘要:更多相關介紹請看這特點僅僅只是虛擬最大限度減少與的交互類似于使用操作單向數據流很大程度減少了重復代碼的使用組件化可組合一個組件易于和其它組件一起使用,或者嵌套在另一個組件內部。在使用后,就變得很容易維護,而且數據流非常清晰,容易解決遇到的。 歡迎移步我的博客閱讀:《React 入門實踐》 在寫這篇文章之前,我已經接觸 React 有大半年了。在初步學習 React 之后就正式應用到項...
摘要:提供,監控的觸發以及的變化。在動態內容的最外層應該使用進行包裹,接收作為參數,注意是一個函數并不是。將作為往子節點進行傳遞,并實現的熱替換。只需要聲明初始狀態以及在接收到之后的改變規則就可以了。 @(blog) Redux basic tutorial 本文的讀者為了解flux概念,熟悉react,了解es6語法的同學 redux 是最近很火的一個 flux 框架,短短的...
閱讀 3128·2021-09-22 15:50
閱讀 3330·2021-09-10 10:51
閱讀 3143·2019-08-29 17:10
閱讀 2918·2019-08-26 12:14
閱讀 1835·2019-08-26 12:00
閱讀 932·2019-08-26 11:44
閱讀 652·2019-08-26 11:44
閱讀 2817·2019-08-26 11:41