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"
}

你可能感兴趣的:(Redux源码阅读(二)——combineReducers)