The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to createStore.
return ( `Given action ${actionName}, reducer "${key}" returned undefined. ` + `To ignore an action, you must explicitly return the previous state.` ) }
// state 和 reducer 检查: // 1. reducers 至少有一个成员 reducer,不能使空对象 // 2. inputState 必须是简单对象 // 3. inputState 中的属性,必须在 reducers 中存在同属性名的 reducer functiongetUnexpectedStateShapeWarningMessage(inputState, reducers, action) { var reducerKeys = Object.keys(reducers) var argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer'
// combineReducers 接收的参数对象至少需要又有一个 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.' ) }
// inputState 是否是一个简单对象 // 简单对象是指 通过 "{}" 或者 "new Object" 创建的键值对的集合 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('", "')}"` ) }
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.` ) } }
// reducers 合法性检查函数,主要检查要求以下几点: // 1. 调用 reducer 返回值不允许为 undefined // 2. reducer 在初次调用时,即被传入类型为 ActionTypes.INIT 的 action 需要对 state 初始化 // 3. 不要处理 redux/* 这个命名空间下的action 直接返回 currentState, // 这一点代码未做检查,但是需要注意,自定义的 action type 最好不要用这个命名空间 functionassertReducerSanity(reducers) { Object.keys(reducers).forEach(key => { var reducer = reducers[key] var initialState = reducer(undefined, { type: ActionTypes.INIT }) // 此处检查 reducer 是否处理值为 undefined 的 state,如果未处理,即 reducer 返回 undefined 抛出异常 // 按照约定,当传递给reducer的state为 undefined 时,通常在初始化的时候,reducer第一次被调用时, // state 为 undefined, 此时 reducer 需要给 state 一个默认值, 而不能返回 undefined。 if (typeof initialState === 'undefined') { thrownewError( `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.` ) }
// 声明一种随机的 action type,确保不会有人使用这种action // 使用这么奇怪的 action 是为了检查,当传入 reducer 不认识的 action type 时,reducer 会不应该返回 undefined。 // 实际上,按照约定,当传入不认识的 action type 或者想要忽视的action type 时,返回 current state 即可 // 同时,警告信息还指出不应该处理任何 redux/* ,命名空间下的 action,除非 current state 为 undefined 的时候,需要做初始化。 var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.') if (typeof reducer(undefined, { type }) === 'undefined') { thrownewError( `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.` ) } }) }
/** * 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. */ exportdefaultfunctioncombineReducers(reducers) { var reducerKeys = Object.keys(reducers) var finalReducers = {}
// 把多个 reducers 合并到 finalReducers 对象中 for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i] if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } var finalReducerKeys = Object.keys(finalReducers)
// 对 reducers 合法性进行检查 var sanityError try { assertReducerSanity(finalReducers) } catch (e) { sanityError = e }