Created
September 11, 2019 18:13
-
-
Save derindutz/b90adc31e00bc2ada2c7f878990e0892 to your computer and use it in GitHub Desktop.
Modification of combineReducers that allows for a global state reducer in addition to slice reducers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Prints a warning in the console if it exists. | |
* | |
* @param {String} message The warning message. | |
* @returns {void} | |
*/ | |
function warning(message) { | |
/* eslint-disable no-console */ | |
if (typeof console !== 'undefined' && typeof console.error === 'function') { | |
console.error(message); | |
} | |
/* eslint-enable no-console */ | |
try { | |
// This error was thrown as a convenience so that if you enable | |
// "break on all exceptions" in your console, | |
// it would pause the execution at this line. | |
throw new Error(message); | |
} catch (e) {} // eslint-disable-line no-empty | |
} | |
function getUndefinedStateErrorMessage(key, action) { | |
const actionType = action && action.type; | |
const actionDescription = (actionType && `action "${String(actionType)}"`) || 'an action'; | |
return ( | |
`Given ${actionDescription}, reducer "${key}" returned undefined. ` + | |
`To ignore an action, you must explicitly return the previous state. ` + | |
`If you want this reducer to hold no value, you can return null instead of undefined.` | |
); | |
} | |
/** | |
* Calls the globalStateReducer and then calls something very similar to combineReducers on sliceReducers. | |
* | |
* Implementation based on https://github.com/reduxjs/redux/blob/master/src/combineReducers.js | |
*/ | |
export function combineSliceReducersWithGlobalStateReducer({ sliceReducers, globalStateReducer }) { | |
const sliceReducerKeys = Object.keys(sliceReducers); | |
const finalSliceReducers = {}; | |
for (let i = 0; i < sliceReducerKeys.length; i++) { | |
const key = sliceReducerKeys[i]; | |
if (process.env.NODE_ENV !== 'production') { | |
if (typeof sliceReducers[key] === 'undefined') { | |
warning(`No reducer provided for key "${key}"`); | |
} | |
} | |
if (typeof sliceReducers[key] === 'function') { | |
finalSliceReducers[key] = sliceReducers[key]; | |
} | |
} | |
const finalSliceReducerKeys = Object.keys(finalSliceReducers); | |
return function combination(state = {}, action) { | |
let hasSliceStateChanged = false; | |
const nextSliceState = {}; | |
for (let i = 0; i < finalSliceReducerKeys.length; i++) { | |
const key = finalSliceReducerKeys[i]; | |
const reducer = finalSliceReducers[key]; | |
const previousSliceStateForKey = state[key]; | |
const nextSliceStateForKey = reducer(previousSliceStateForKey, action); | |
if (typeof nextSliceStateForKey === 'undefined') { | |
const errorMessage = getUndefinedStateErrorMessage(key, action); | |
throw new Error(errorMessage); | |
} | |
nextSliceState[key] = nextSliceStateForKey; | |
hasSliceStateChanged = | |
hasSliceStateChanged || nextSliceStateForKey !== previousSliceStateForKey; | |
} | |
const nextGlobalState = globalStateReducer(state, action); | |
if (typeof nextGlobalState === 'undefined') { | |
throw new Error(getUndefinedStateErrorMessage('globalStateReducer', action)); | |
} | |
const hasGlobalStateChanged = nextGlobalState !== state; | |
if (hasSliceStateChanged && hasGlobalStateChanged) { | |
return { ...nextGlobalState, ...nextSliceState }; | |
} else if (hasSliceStateChanged) { | |
return { ...state, ...nextSliceState }; | |
} else if (hasGlobalStateChanged) { | |
return nextGlobalState; | |
} else { | |
return state; | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment