Source: scopeReducer.js

const isActionTypeScoped = (scope, actionType) => {
  const splittedActionType = actionType.split("/");
  return splittedActionType.length >= 3 && splittedActionType[1] === scope;
};

/**
 * Scopes a reducer function.
 * 
 * Accepts a scope and a reducer function and returns a scoped reducer fucnction. A scoped reducer works and can be used like a regular reducer - the only difference is that the scoped reducer will ignore all actions that aren't scoped the same.
 * If not of type "function", the given reducer is returned as it is.
 * 
 * @example
 * 
 * const setValue = payload => ({
 *  type: "app/reducerA/SET_VALUE",
 *  payload
 * })
 * 
 * const scopedSetValue = scopeAction("reducerB", setValue)
 * 
 * const reducer = (state = 0, action) => action.type === "app/reducerA/SET_VALUE" ? action.payload : state
 * const scopedReducer = scopeReducer("reducerB", reducer)
 * 
 * // scoped reducer ignores unscoped action
 * console.log(scopedReducer(0, setValue(42)))
 * // 0
 * 
 * // scoped reducer handles scoped action
 * console.log(scopedReducer(0, scopedSetValue(42)))
 * // 42
 * 
 * @param {string} scope 
 * @param {function} reducer
 * @returns {function}
 */

const scopeReducer = (scope, reducer) =>
  scope && typeof reducer === "function"
    ? (state, action) => {
        const shouldProcessAction =
          action.meta &&
          action.meta.unscopedActionType &&
          isActionTypeScoped(scope, action.type);
        return shouldProcessAction
          ? reducer(state, { ...action, type: action.meta.unscopedActionType })
          : reducer(state, {});
      }
    : reducer;

export default scopeReducer;