根据Redux文档学源码(二)

198 阅读4分钟

今天我们继续来根据Redux的文档来学习其源码,前面我们讲了Redux的createStoreAPI,今天我们来看看另一个我们日常比较常用的APIcombineReducers

首先我们都知道combineReducers的作用就是将拆分的小的reducer合并为一个大的reducer,官网的示例是这样子的

import { combineReducers } from 'redux';

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp;

👌下面我们深入源码看看combineReducers究竟做了啥

/**
 * Turns an object whose values are different reducer functions, into a single
 * reducer function. It will call every child reducer, and gather their results
 * into a single state object, whose keys correspond to the keys of the passed
 * reducer functions.
 *
 * @param {Object} reducers An object whose values correspond to different
 * reducer functions that need to be combined into one. One handy way to obtain
 * it is to use ES6 `import * as reducers` syntax. The reducers may never return
 * undefined for any action. Instead, they should return their initial state
 * if the state passed to them was undefined, and the current state for any
 * unrecognized action.
 *
 * @returns {Function} A reducer function that invokes every reducer inside the
 * passed object, and builds a state object with the same shape.
 */
export default function combineReducers(reducers) {
  var finalReducers = pick(reducers, (val) => typeof val === 'function');

  Object.keys(finalReducers).forEach(key => {
    var reducer = finalReducers[key];
    if (typeof reducer(undefined, { type: ActionTypes.INIT }) === '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.`
      );
    }

    var type = Math.random().toString(36).substring(7).split('').join('.');
    if (typeof reducer(undefined, { type }) === '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.`
      );
    }
  });

  var defaultState = mapValues(finalReducers, () => undefined);
  var stateShapeVerified;

  return function combination(state = defaultState, action) {
    var finalState = mapValues(finalReducers, (reducer, key) => {
      var newState = reducer(state[key], action);
      if (typeof newState === 'undefined') {
        throw new Error(getErrorMessage(key, action));
      }
      return newState;
    });

    if (process.env.NODE_ENV !== 'production') {
      if (!stateShapeVerified) {
        verifyStateShape(state, finalState);
        stateShapeVerified = true;
      }
    }

    return finalState;
  };
}

粗看以下代码量还是比较多的但是去除里面的一些Error提示之后,其实也没有几行,我们一行一行来分析下。 首先从注释代码上看其传入值是一个Object而且Object对象的每个值都是一个reducer函数;返回值是一个调用了传入子reducer且生成一个state对象的函数。

step1

var finalReducers = pick(reducers, (val) => typeof val === 'function'); 这里的pick函数在源码中已经引入过,总的来说作用就是,挑选出对象中满足第二个传入函数条件的值,并返回由满足条件的值生成的一个新对象,在这儿的作用就是,选出reducer中value类型为函数的值并赋值给finalState。

step2

finalReducers进行遍历,首先遇到的两个if判断是判断当我们传入的默认state为undefined的时候,如果actionType是init或者是一个随机的type那么,返回的值不应该是undefined。否则报错

setp3

var defaultState = mapValues(finalReducers, () => undefined); mapValues为源码中import的函数,作用是对finalReducers中的每个value值应用,传入的函数并且,返回最终的Object。比如说Redux文档中的示例经过这一步返回的就是

{
  visibilityFilter:undefined,
  todos:undefined
}

step4

var stateShapeVerified; 定义一个stateShapeVerifiedflag

####step5 下面就是最关键的组合函数了,也就是combineReducers最终会返回给我们的函数,我们看看返回的这个函数究竟有什么作用,首先他将,defaultState作为了state的默认值。接下看看这个函数

var finalState = mapValues(finalReducers, (reducer, key) => {
      var newState = reducer(state[key], action);
      if (typeof newState === 'undefined') {
        throw new Error(getErrorMessage(key, action));
      }
      return newState;
    });

这里再一次用到了mapValues这个工具函数,在工具函数里面主要的逻辑就是,对传入的finalReducers,的每个值执行,传入的函数,然后返回这个一个新的对象,那么在这个代码里面我们可以看到,传入的函数的作用就是对传入的action执行每个reducer并得到更新后的state对象,那么这整个函数的作用也就是得到了一个新的state。同时这也解释了为什么我们说,发出的action是全局的,因为一个action是要在全部的子reducer中都要执行一遍。

finaly

if (process.env.NODE_ENV !== 'production') {
      if (!stateShapeVerified) {
        verifyStateShape(state, finalState);
        stateShapeVerified = true;
      }
    }

最后这个函数主要是对返回的state和初始state的key进行比较看看又没有违规的key出现,如果有则报错。

最后:由于语言功底差可能解释的不到位,如果有看不懂的欢迎留言询问。