combineReducers理解

2019-07-30  本文已影响0人  SmileMylife

1.combineReducers旨在整合切分后reducer,并将state树切分至各个reduce进行管理,这样做的好处是每个reduce只用管理它自己的state内容,此时state树中的结构变成{reducerName: reducerStateBySelf},即key为redcuer函数的名字,value为reducer各自所管理的state。

2.combineReducer的原理:
首先combineReducers需要传入切分的reducer对象,然后combineReducers内部会对传入对象进行遍历,然后会执行各个reducer函数,由于此时actionType为undefined,所以各reducer会返回默认的state,传入对象遍历完毕后,会在combineReducers组合成一个新的state树对象返回,这个state树的结构如1中所说。然后当触发某个action操作时,也是会执行组合后的reducer函数,然后在函数内部再进行遍历,每个reducer都会再执行一次,然后根据相应的action做相应的操作,更新对应reducer所管理的state。(注:此时actionType应该是唯一的值,否则的话可能引起拥有相同action的reducer同时进行更新)。

以下是combineReducers函数源码,可帮助理解:

//注意首次在调用该方法的时候传入的state对象是空对象,由于根据reducer的key取值的时候都是undefined,所以会返回相应reducer的默认state。
function combineReducers(reducers) {
  var reducerKeys = Object.keys(reducers);
  var finalReducers = {};

  for (var i = 0; i < reducerKeys.length; i++) {
    var key = reducerKeys[i];

//如果是生产环境,如果传入的reducer是undefined则报警告
    if (process.env.NODE_ENV !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        warning("No reducer provided for key \"" + key + "\"");
      }
    }

//如果传入的object的value是函数,即为reduce函数,则将函数复制一下
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key];
    }
  }

  var finalReducerKeys = Object.keys(finalReducers);
  var unexpectedKeyCache;

  if (process.env.NODE_ENV !== 'production') {
    unexpectedKeyCache = {};
  }

  var shapeAssertionError;

  try {
    assertReducerShape(finalReducers);
  } catch (e) {
    shapeAssertionError = e;
  }

  return function combination(state, action) {
    if (state === void 0) {    //void 0 即为undefined
      state = {};
    }

    if (shapeAssertionError) {
      throw shapeAssertionError;
    }

    if (process.env.NODE_ENV !== 'production') {
      var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);

      if (warningMessage) {
        warning(warningMessage);
      }
    }

    var hasChanged = false;    //用来标记各reducer执行过后state是否改变
    var nextState = {};    //用于返回新的state树

//遍历过滤后的存放reducer的对象
    for (var _i = 0; _i < finalReducerKeys.length; _i++) {
      var _key = finalReducerKeys[_i];
      var reducer = finalReducers[_key];
      var previousStateForKey = state[_key];    //获取到各reduce
      var nextStateForKey = reducer(previousStateForKey, action);    //执行完相应的reduce后返回新的state

      if (typeof nextStateForKey === 'undefined') {
        var errorMessage = getUndefinedStateErrorMessage(_key, action);
        throw new Error(errorMessage);
      }

      nextState[_key] = nextStateForKey;    //将state更新到总的state树
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
    }

    return hasChanged ? nextState : state;    //根据state树是否发生变化返回前state或修改后的state
  };
}

//此函数为了校验reduce是否符合标准,是否有初始化的state值。
function assertReducerShape(reducers) {
  Object.keys(reducers).forEach(function (key) {
    var reducer = reducers[key];
    var initialState = reducer(undefined, {
      type: ActionTypes.INIT
    });

    if (typeof initialState === '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. If you don't want to set a value for this reducer, " + "you can use null instead of undefined.");
    }

    if (typeof reducer(undefined, {
      type: ActionTypes.PROBE_UNKNOWN_ACTION()
    }) === '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, but can be null.");
    }
  });
}
上一篇 下一篇

猜你喜欢

热点阅读