Combined Reducers
Combined Reducers does combining reducers. That's it. The Most of codes on combineReducers.js is error handling.
Here is the steps of combining reducers.
- Get keys of each reducers.
- Iterate reducers and check wether reducers have keys and are functions.
- Put valid reducers to
finalReducers
object. - Return function
combination()
which will be invoked every time action is dispatched. - From
combination()
,finalReducers
will be iterated and each reducers will be invoked and the store new state on `newState` object by key. - It tracks state was changed and compare new entire state
newState
and previous entire statepreviousState
. If it was changed, return new statenewState
.
// 1. get keys of reducers
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
// 2. Iterate reducers and check reducers have keys and are functions
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}"`)
}
}
// If it is function or not a assign to final reducers
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
// 3. Extract keys from 'finalReducers' object
const finalReducerKeys = Object.keys(finalReducers)
/*
Error handling
*/
let unexpectedKeyCache
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
let shapeAssertionError
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
/*
- combination
@params state
@params action
*/
return function combination(state = {}, action) {
/*
Error handling phase
*/
if (shapeAssertionError) {
throw shapeAssertionError
}
if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(
state,
finalReducers,
action,
unexpectedKeyCache
)
if (warningMessage) {
warning(warningMessage)
}
}
let hasChanged = false
// Store new state
const nextState = {}
/*
Iterate finalReducer and execute each reducers
*/
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
/*
Getting next state state by running reducer function.
function reducer(state = {}, action) {
switch(aciton.type): {
case: ''
return Object.assing({}, state, action.payload)
}
}
*/
const nextStateForKey = reducer(previousStateForKey, action)
// reducer function returns new state and nextStateForKey has new state.
// If it is undefined, throw Error.
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
// Put nextStateForkey to new state object
nextState[key] = nextStateForKey
// If it is different object from previousStateForKey assign true to hasChanged
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
// if it is changed, return newState
return hasChanged ? nextState : state
}
}