130 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			130 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import { ActionTypes } from './createStore';
 | 
						|
import isPlainObject from 'lodash-es/isPlainObject';
 | 
						|
import warning from './utils/warning';
 | 
						|
 | 
						|
function getUndefinedStateErrorMessage(key, action) {
 | 
						|
  var actionType = action && action.type;
 | 
						|
  var actionName = actionType && '"' + actionType.toString() + '"' || 'an action';
 | 
						|
 | 
						|
  return 'Given action ' + actionName + ', 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.';
 | 
						|
}
 | 
						|
 | 
						|
function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
 | 
						|
  var reducerKeys = Object.keys(reducers);
 | 
						|
  var argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer';
 | 
						|
 | 
						|
  if (reducerKeys.length === 0) {
 | 
						|
    return 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.';
 | 
						|
  }
 | 
						|
 | 
						|
  if (!isPlainObject(inputState)) {
 | 
						|
    return 'The ' + argumentName + ' has unexpected type of "' + {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + '". Expected argument to be an object with the following ' + ('keys: "' + reducerKeys.join('", "') + '"');
 | 
						|
  }
 | 
						|
 | 
						|
  var unexpectedKeys = Object.keys(inputState).filter(function (key) {
 | 
						|
    return !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key];
 | 
						|
  });
 | 
						|
 | 
						|
  unexpectedKeys.forEach(function (key) {
 | 
						|
    unexpectedKeyCache[key] = true;
 | 
						|
  });
 | 
						|
 | 
						|
  if (unexpectedKeys.length > 0) {
 | 
						|
    return 'Unexpected ' + (unexpectedKeys.length > 1 ? 'keys' : 'key') + ' ' + ('"' + unexpectedKeys.join('", "') + '" found in ' + argumentName + '. ') + 'Expected to find one of the known reducer keys instead: ' + ('"' + reducerKeys.join('", "') + '". Unexpected keys will be ignored.');
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function assertReducerShape(reducers) {
 | 
						|
  Object.keys(reducers).forEach(function (key) {
 | 
						|
    var reducer = reducers[key];
 | 
						|
    var 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.');
 | 
						|
    }
 | 
						|
 | 
						|
    var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.');
 | 
						|
    if (typeof reducer(undefined, { type: 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.');
 | 
						|
    }
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Turns an object whose values are different reducer functions, into a single
 | 
						|
 * reducer function. It will call every child reducer, and gather their results
 | 
						|
 * into a single state object, whose keys correspond to the keys of the passed
 | 
						|
 * reducer functions.
 | 
						|
 *
 | 
						|
 * @param {Object} reducers An object whose values correspond to different
 | 
						|
 * reducer functions that need to be combined into one. One handy way to obtain
 | 
						|
 * it is to use ES6 `import * as reducers` syntax. The reducers may never return
 | 
						|
 * undefined for any action. Instead, they should return their initial state
 | 
						|
 * if the state passed to them was undefined, and the current state for any
 | 
						|
 * unrecognized action.
 | 
						|
 *
 | 
						|
 * @returns {Function} A reducer function that invokes every reducer inside the
 | 
						|
 * passed object, and builds a state object with the same shape.
 | 
						|
 */
 | 
						|
export default function combineReducers(reducers) {
 | 
						|
  var reducerKeys = Object.keys(reducers);
 | 
						|
  var finalReducers = {};
 | 
						|
  for (var i = 0; i < reducerKeys.length; i++) {
 | 
						|
    var key = reducerKeys[i];
 | 
						|
 | 
						|
    if (process.env.NODE_ENV !== 'production') {
 | 
						|
      if (typeof reducers[key] === 'undefined') {
 | 
						|
        warning('No reducer provided for key "' + key + '"');
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (typeof reducers[key] === 'function') {
 | 
						|
      finalReducers[key] = reducers[key];
 | 
						|
    }
 | 
						|
  }
 | 
						|
  var finalReducerKeys = Object.keys(finalReducers);
 | 
						|
 | 
						|
  var unexpectedKeyCache = void 0;
 | 
						|
  if (process.env.NODE_ENV !== 'production') {
 | 
						|
    unexpectedKeyCache = {};
 | 
						|
  }
 | 
						|
 | 
						|
  var shapeAssertionError = void 0;
 | 
						|
  try {
 | 
						|
    assertReducerShape(finalReducers);
 | 
						|
  } catch (e) {
 | 
						|
    shapeAssertionError = e;
 | 
						|
  }
 | 
						|
 | 
						|
  return function combination() {
 | 
						|
    var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
 | 
						|
    var action = arguments[1];
 | 
						|
 | 
						|
    if (shapeAssertionError) {
 | 
						|
      throw shapeAssertionError;
 | 
						|
    }
 | 
						|
 | 
						|
    if (process.env.NODE_ENV !== 'production') {
 | 
						|
      var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);
 | 
						|
      if (warningMessage) {
 | 
						|
        warning(warningMessage);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    var hasChanged = false;
 | 
						|
    var nextState = {};
 | 
						|
    for (var _i = 0; _i < finalReducerKeys.length; _i++) {
 | 
						|
      var _key = finalReducerKeys[_i];
 | 
						|
      var reducer = finalReducers[_key];
 | 
						|
      var previousStateForKey = state[_key];
 | 
						|
      var nextStateForKey = reducer(previousStateForKey, action);
 | 
						|
      if (typeof nextStateForKey === 'undefined') {
 | 
						|
        var errorMessage = getUndefinedStateErrorMessage(_key, action);
 | 
						|
        throw new Error(errorMessage);
 | 
						|
      }
 | 
						|
      nextState[_key] = nextStateForKey;
 | 
						|
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
 | 
						|
    }
 | 
						|
    return hasChanged ? nextState : state;
 | 
						|
  };
 | 
						|
} |