事出有因,写 rabbit
的时候,一条单测出了问题
Reducer _object returned undefined during initialization.
想来必然是 reducer
缺少默认的 state
所致,但是我的单测是从 dva
直接拿过来改改就用的,应该不会出现问题,所以一定是哪里出了差错。后面发现了,虽然 dva
可以接受 reducers
是空对象,但应用运行起来有一样问题。
主要是这个问题无伤大雅,model
不会出现只有 reducers
这种情况。我也给出了解决:构建 reducer
给一个默认 state
顶着(但我认为一个 model
应该强制存在 state
)
显然这次并不想讲这个,而是想理解 combineReducers
combineReducers
一共有 4 个方法:
getUndefinedStateErrorMessage
getUnexpectedStateShapeWarningMessage
assertReducerShape
- (主要方法)
combineReducers
getUndefinedStateErrorMessage
很明显,这是一个生成错误信息的方法。主要是限制 reducer
必须返回 state
assertReducerShape
这是一个检查 reducer
是否合规的方法。
首先接受一个集合所有 reducer
的对象进行遍历,每一个抽出来检验,做一次运行尝试,看是否能得到 state
。如果得到的 state
为 undefined
则该 reducer
是非法的。
文中开头提到的问题正是由这里报出,因为我们给出的 reducers
为空对象,且 state
为 undefined
,所以自然会报错。dva
的单测只检查 model
的合理性,且 state
允许任何内容和 reducers
允许空对象,并没有考虑一个 model
同时不存在这两者的情况,不过显然没意义,所以目前是不清楚 dva
团队是没想到还是也觉得无所谓。
combineReducers
这是这组方法中的核心,该方法也作为默认方法导出。这组方法的目的是把一群 reducer
合并为一个方法供 createStore
使用。
上来是两组变量:
// 提取对象中所有字段名,一个 key 对应一个 reducer 方法
const reducerKeys = Object.keys(reducers)
// 由下面可得,该变量用来暂存基本合理(reducer 是一个方法)的 reducer 方法。
const finalReducers = {}
//...
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
// 如果在非生产环境中,还会提示仅有 key 没有方法的值以方便我们修改调试
// 再把初步验证过的 reducer 取出
const finalReducerKeys = Object.keys(finalReducers)
//...
let shapeAssertionError
// 放入刚刚用来验证 reducer 合规的方法进行第二次验证,并做错误处理
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
接着是返回一个新的 reducer
,并对所有传入的合规的 reducer
进行 diff。
// 创建一个对比记录变量以及新 state
let hasChanged = false
const nextState = {}
// 遍历刚刚验证完的 reducer key 数组
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
// 获取当前 state
const previousStateForKey = state[key]
// 通过执行 reducer 得到新的 state
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
// 并将新 reducer 产物绑定到 nextState,key 不变。(方便对比)
nextState[key] = nextStateForKey
// 右式第一个 hasChanged 表达:如果已经为 true 那么就肯定 true,没必要再去对比验证
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
// 返回:有改变的返回新的,无改变返回原 state。
return hasChanged ? nextState : state
所以这组方法目的就是把所有复杂的 reducer
和 state
组成新的状态树,统一管理。业务开发时可以根据需求拆分多个 reducer
和 state
,便于开发和组织。