Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save derindutz/b90adc31e00bc2ada2c7f878990e0892 to your computer and use it in GitHub Desktop.
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.
/**
* 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