redux源码 - combineReducers
soraping opened this issue · comments
caoshiping commented
reducer 指定了状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state
// reducer 样本代码
function todo(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
});
default:
return state;
}
}
combineReducers()
是 redux
提供的工具方法,用来合并多个 reducer
。
combineReducers
import { combineReducers } from "redux";
export default combineReducers({
visibilityFilter,
todos
});
在 createStore
方法中的第一参数就是这个模块。
combineReducers
方法接收的参数就是样板代码中的一个一个的 reducer
,代码解析:
import ActionTypes from "./utils/actionTypes";
import warning from "./utils/warning";
import isPlainObject from "./utils/isPlainObject";
/**
* 经过reducer,返回值不能为undefined
* @param {*} key state树中对应的reducer键值
* @param {*} action
*/
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.`
);
}
/**
*
* @param {state} inputState 当前state树
* @param {*} reducers
* @param {*} action
* @param {*} unexpectedKeyCache
*/
function getUnexpectedStateShapeWarningMessage(
inputState,
reducers,
action,
unexpectedKeyCache
) {
const reducerKeys = Object.keys(reducers);
const 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."
);
}
// state是不是纯对象
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('", "')}"`
);
}
// 过滤出state树中无法识别的,无定义reducer的key
const unexpectedKeys = Object.keys(inputState).filter(
// 判定条件:reducers中不存在 这个 key 且 unexpectedKeyCache变量中也不存在
key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
);
unexpectedKeys.forEach(key => {
// 此处要注意下,这个地方是内部函数的一个副作用,在此处赋值,会对函数外的引用变量发生改变
// 对,又是闭包
unexpectedKeyCache[key] = true;
});
if (action && action.type === ActionTypes.REPLACE) return;
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.`
);
}
}
/**
* 确认reducer是否是合法的reducer,即返回的state是不是undefined,如果是undefined,则是非法reducer
*
* 1. 在初始化阶段,reducer 传入的 state 值是 undefined,此时,需要返回初始state,且初始state不能为undefined
* 2. 当传入不认识的 actionType 时, reducer(state, {type}) 返回的不能是undefined
* 3. redux/ 这个 namespace 下的action 不应该做处理,直接返回 currentState 就行 (谁运气这么差会去用这种actionType...)
*/
function assertReducerShape(reducers) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key];
const 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.`
);
}
if (
typeof reducer(undefined, {
type: ActionTypes.PROBE_UNKNOWN_ACTION()
}) === "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.`
);
}
});
}
/**
* 合并reducer
* 接收参数:
* {
* todo: (state, action) => {
* ...
* return state;
* },
* ...
* }
* @param {*} reducers
*
* 返回值是一个纯函数,接收当前 state树和action,这个纯函数返回更新后的 state 树
* @return (state, action) => state
*/
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers);
const finalReducers = {};
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
if (process.env.NODE_ENV !== "production") {
if (typeof reducers[key] === "undefined") {
warning(`No reducer provided for key "${key}"`);
}
}
// 把非function的reducer过滤掉,存入变量finalReducers
if (typeof reducers[key] === "function") {
finalReducers[key] = reducers[key];
}
}
const finalReducerKeys = Object.keys(finalReducers);
// 定义变量存储 state树中的无效key(reducer中不存在)
let unexpectedKeyCache;
if (process.env.NODE_ENV !== "production") {
unexpectedKeyCache = {};
}
let shapeAssertionError;
try {
// 校验reducer是否合法
assertReducerShape(finalReducers);
} catch (e) {
shapeAssertionError = e;
}
/**
* 最终返回的函数,接收两个参数,在createStore方法中会调用,state为当前树,action为传递的操作
* @return state 经过 action 改变后的 state 树
*/
return function combination(state = {}, action) {
// 抛出不合法的 reducer 校验
if (shapeAssertionError) {
throw shapeAssertionError;
}
if (process.env.NODE_ENV !== "production") {
const warningMessage = getUnexpectedStateShapeWarningMessage(
state,
finalReducers,
action,
unexpectedKeyCache
);
if (warningMessage) {
warning(warningMessage);
}
}
// 是否有改变判定字段
let hasChanged = false;
const nextState = {};
for (let i = 0; i < finalReducerKeys.length; i++) {
// state树中的各个reducer对应的值
const key = finalReducerKeys[i];
// 单个reducer
const reducer = finalReducers[key];
// 此处是state树中对应的状态值,其key,就是reducers传入combineRefucers所对应各个reducer的key值
const previousStateForKey = state[key];
// 经过reducer,返回改变后的模块state值
const nextStateForKey = reducer(previousStateForKey, action);
// reducer的返回值不能为undefined
if (typeof nextStateForKey === "undefined") {
const errorMessage = getUndefinedStateErrorMessage(key, action);
throw new Error(errorMessage);
}
// 找到对应的key值,把改变后的模块state重新赋值
nextState[key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
return hasChanged ? nextState : state;
};
}