Redux源码阅读(二)——combineReducers

调试项目
【Redux源码阅读(二)——combineReducers】仓库:https://github.com/reduxjs/react-redux
项目:examples/todos
combineReducers
调试代码

import { combineReducers } from 'redux' import todos from './todos' import visibilityFilter from './visibilityFilter'export default combineReducers({ todos, visibilityFilter })

源码
export default function combineReducers(reducers: ReducersMapObject) { const reducerKeys = Object.keys(reducers) const finalReducers: ReducersMapObject = {} 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}"`) } }if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers)// This is used to make sure we don't warn about the same // keys multiple times. let unexpectedKeyCache: { [key: string]: true } if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} }let shapeAssertionError: Error try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e }return function combination( state: StateFromReducersMapObject = {}, action: AnyAction ) { 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: StateFromReducersMapObject = {} 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) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length return hasChanged ? nextState : state } }

调用combineReducers
  • combineReducers接收一个对象reducers,然后遍历reducers,筛出valuefunction的放入finalReducers
    finalReducers的结构在这个例子里是这样的
{ todos:function(){…}, visibilityFilter:function(){…} }

  • 使用assertReducerShape来校验,调用reducer时有没有传出默认的state
  • return combination函数作为新的reducer
调用生成的新的reducer 就是调用上面提到的combination函数
  • 调用场景有主动调用和初始化调用。上篇说到,最开始的时候redux将会调用一次reducer去获取state的初始值;
  • 使用传入的action,依次调用finalReducers对象里的方法。
    也就是说,如果我们刚开始传入的todos、 visibilityFilter这两个reducers这里面有相同的action.type,那么逻辑将会依次被触发。
var _key = finalReducerKeys[_i]; var reducer = finalReducers[_key]; var previousStateForKey = state[_key]; var nextStateForKey = reducer(previousStateForKey, action);

值得注意的是,在调用的时候传入各个reducer的state仅限于state[key],而不是整个state
  • 设定调用之后的state
nextState[_key] = nextStateForKey;

可以从这里得知,每个传入的reducer都对应同名的statekey,他们return的新state只会对state[key]value做更新
  • 执行结果
{ todos: [] visibilityFilter: "SHOW_ALL" }

    推荐阅读