歡迎關注redux源碼分析系列文章:
redux源碼分析之一:createStore.js
redux源碼分析之二:combineReducers.js
redux源碼分析之三:bindActionCreators.js
redux源碼分析之四:compose.js
redux源碼分析之五:applyMiddlewaregit
combineReducers.js文件對外暴露了一個函數combineReducers,combineReducer函數是redux的一個輔助性的函數,用於拆分createStore裏面的第一個參數:reducer函數。combineReducer函數的返回值是一個函數,該函數是組合以後的一個標準的reducer函數。github
combineReducers函數僅包含一個參數reducers,reducers是一個object類型參數,好比:redux
let reducers = { users: function getUsersReducer(){}, userInfo: function getUserInfoReducer(){} }
一、從傳入的參數裏面提取出合法的reducers(reducers的每個key對應的value值是函數,纔是合法的子reducer),賦值給新的局部變量:finalReducerssegmentfault
const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } //過濾出reducers對應的value值是function的key,將其放入finalReducers對象 if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } }
二、校驗finalReducers, 判斷其每個子reducer是否能返回正常的子stateapp
//取出過濾出來的有效的keys列表 const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } let shapeAssertionError try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e }
assertReducerShape函數:less
//確認reducer是不是合法的reducer,即返回的state是否是undefined,若是是undefined,則是非法reducer function assertReducerShape(reducers) { Object.keys(reducers).forEach(key => { const reducer = reducers[key] const initialState = reducer(undefined, {type: ActionTypes.INIT}) if (typeof initialState === 'undefined') { throw new Error( `Reducer "${key}" returned undefined during initialization. ` + `If the state passed to the reducer is undefined, you must ` + `explicitly return the initial state. The initial state may ` + `not be undefined. If you don't want to set a value for this reducer, ` + `you can use null instead of undefined.` ) } const type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.') if (typeof reducer(undefined, {type}) === 'undefined') { throw new Error( `Reducer "${key}" returned undefined when probed with a random type. ` + `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` + `namespace. They are considered private. Instead, you must return the ` + `current state for any unknown actions, unless it is undefined, ` + `in which case you must return the initial state, regardless of the ` + `action type. The initial state may not be undefined, but can be null.` ) } }) }
函數combination是一個標準的reducer函數,有初始化的state參數,和一個攜帶了actionType和數據的action對象。dom
function combination(state = {}, action) { //若是有非法的reducer,就直接報錯嘍 if (shapeAssertionError) { throw shapeAssertionError } if (process.env.NODE_ENV !== 'production') { const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache) if (warningMessage) { warning(warningMessage) } } let hasChanged = false //定義新的nextState const nextState = {} // 1,遍歷reducers對象中的有效key, // 2,執行該key對應的value函數,即子reducer函數,並獲得對應的state對象,即子state // 3,將新的子state掛到新的nextState對象上,key不變 for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey //若是hasChanged爲true,那就是true了 後面的判斷是,只要有一次nextStateForKey!== previousStateForKey不一樣,就說明整個state不一樣 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } //若是state發生變化了,直接返回新的nextState,不然,仍是返回舊的state return hasChanged ? nextState : state } }
combineReducers函數其實就實現一個功能:將一個複雜的父reducer函數,根據state狀態對應的key,拆分紅幾個子reducer;每一個子reducer返回一個子state。多層嵌套的reducer樹,能夠對應組成一個多層嵌套的state狀態樹。ide
完整解析請參考個人github:https://github.com/abczhijia/...,若是對您有幫助,歡迎star,有任何問題也請指正。函數