简介
随着应用变得复杂,需要对 reducer 函数 进行拆分,拆分后的每一块独立负责管理 state 的一部分。
Redux 中的 combineReducers 能让我们很方便地把多个 reducers 组合起来,成为一个新的 reducer。
原理
combineReducers辅助函数的作用是,把一个由多个不同 reducer 函作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore
。
合并后的 reducer 可以调用各个子 reducer,并把它们的结果合并成一个 state 对象。state 对象的结构由传入的多个 reducer 的 key 决定。
state的最终结构为:
{
reducer1: ...,
reducer2: ...
}
通过上述的讲解我们可以知道,combineReducecrs的基本型态应该是这样的:
function combineReducers(reducers) {
return function (state, action) { /*...*/ };
}
它接受 reducers 作为参数,然后返回一个标准的 reducer 函数。
下面我们就来看一下combineReducers的源码解析:
import { ActionTypes } from './createStore'
import isPlainObject from 'lodash/isPlainObject'
import warning from './utils/warning'
//根据key和action生成错误信息
function getUndefinedStateErrorMessage(key, action) {
//...
}
//一些警告级别的错误
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'
//判断reducers是否为空数组
//判断state是否是对象
//给state中存在而reducer中不存在的属性添加缓存标识并警告
//...
}
//这个方法用于检测用于组合的reducer是否是符合redux规定的reducer
function assertReducerSanity(reducers) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key]
//调用reducer方法,undefined为第一个参数
//使用前面说到过的ActionTypes.INIT和一个随机type生成action作为第二个参数
//若返回的初始state为undefined,则这是一个不符合规定的reducer方法,抛出异常
//...
})
}
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}"`)
}
}
//finalReducers是过滤后的reducers,它的每一个属性都是一个function
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
let unexpectedKeyCache
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
let sanityError
//检测每个reducer是否是符合标准的reducer
try {
assertReducerSanity(finalReducers)
} catch (e) {
sanityError = e
}
return function combination(state = {}, action) {
if (sanityError) {
throw sanityError
}
//如果不是成产环境,做一些警告判断
if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)
if (warningMessage) {
warning(warningMessage)
}
}
let hasChanged = false
const nextState = {} //下一个state树
//遍历所有reducers,然后将每个reducer返回的state组合起来生成一个大的状态树,所以任何action,redux都会遍历所有的reducer
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
//如果此reducer返回的新的state是undefined,抛出异常
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
//如果当前action对应的reducer方法执行完后,该处数据没有变化,则返回原来的流程树
return hasChanged ? nextState : state
}
}
源码解释:
- 这是一个标准的 reducer 函数,接受一个初始化状态 和 一个 action 参数;
- 每次调用的时候会去遍历
finalReducers
(有效的 reducer 列表),获取列表中每个 reducer 对应的先前状态:var previousStateForKey = state[key]
; - 看到这里就应该明白传入的
reducers
组合为什么key
要和 store 里面的 state 的key
相对应; - 然后得到当前遍历项的下一个状态:
var nextStateForKey = reducer(previousStateForKey, action)
; - 然后把它添加到整体的下一个状态:
nextState[key] = nextStateForKey
- 每次遍历会判断整体状态是否改变:
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
- 在最后,如果没有改变就返回原有状态,如果改变了就返回新生成的状态对象:
return hasChanged ? nextState : state
。
网友评论