redux
Version:
Predictable state container for JavaScript apps
114 lines (87 loc) • 4.78 kB
JavaScript
;
exports.__esModule = true;
exports['default'] = combineReducers;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _createStore = require('../createStore');
var _utilsIsPlainObject = require('../utils/isPlainObject');
var _utilsIsPlainObject2 = _interopRequireDefault(_utilsIsPlainObject);
var _utilsMapValues = require('../utils/mapValues');
var _utilsMapValues2 = _interopRequireDefault(_utilsMapValues);
var _utilsPick = require('../utils/pick');
var _utilsPick2 = _interopRequireDefault(_utilsPick);
/* eslint-disable no-console */
function getErrorMessage(key, action) {
var actionType = action && action.type;
var actionName = actionType && '"' + actionType.toString() + '"' || 'an action';
return 'Reducer "' + key + '" returned undefined handling ' + actionName + '. ' + 'To ignore an action, you must explicitly return the previous state.';
}
function verifyStateShape(initialState, currentState) {
var reducerKeys = Object.keys(currentState);
if (reducerKeys.length === 0) {
console.error('Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.');
return;
}
if (!_utilsIsPlainObject2['default'](initialState)) {
console.error('initialState has unexpected type of "' + ({}).toString.call(initialState).match(/\s([a-z|A-Z]+)/)[1] + '". Expected initialState to be an object with the following ' + ('keys: "' + reducerKeys.join('", "') + '"'));
return;
}
var unexpectedKeys = Object.keys(initialState).filter(function (key) {
return reducerKeys.indexOf(key) < 0;
});
if (unexpectedKeys.length > 0) {
console.error('Unexpected ' + (unexpectedKeys.length > 1 ? 'keys' : 'key') + ' ' + ('"' + unexpectedKeys.join('", "') + '" in initialState will be ignored. ') + ('Expected to find one of the known reducer keys instead: "' + reducerKeys.join('", "') + '"'));
}
}
/**
* 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.
*/
function combineReducers(reducers) {
var finalReducers = _utilsPick2['default'](reducers, function (val) {
return typeof val === 'function';
});
Object.keys(finalReducers).forEach(function (key) {
var reducer = finalReducers[key];
if (typeof reducer(undefined, { type: _createStore.ActionTypes.INIT }) === '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.');
}
var type = 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 ' + _createStore.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.');
}
});
var defaultState = _utilsMapValues2['default'](finalReducers, function () {
return undefined;
});
var stateShapeVerified;
return function combination(state, action) {
if (state === undefined) state = defaultState;
var finalState = _utilsMapValues2['default'](finalReducers, function (reducer, key) {
var newState = reducer(state[key], action);
if (typeof newState === 'undefined') {
throw new Error(getErrorMessage(key, action));
}
return newState;
});
if (process.env.NODE_ENV !== 'production') {
if (!stateShapeVerified) {
verifyStateShape(state, finalState);
stateShapeVerified = true;
}
}
return finalState;
};
}
module.exports = exports['default'];