redux源码分析:dispatch中为什么需要 isDispatching

同步函数中,出现 isDispatching 的意义何在
更新于: 2021-12-19 12:57:28

先说重点,下面是源码里的结论

不要在 reducer 里执行 store.dispatch: Reducers may not dispatch actions. 

自己的看法

  1. reducer 里不要有 store.dispatch 有了 isDispatching 就杜绝了 reducer 里的各种”骚“操作的出现,从 性能/BUG 两方面都有好处
  2. 针对 store.dispatch 这个情况,我看到一种说法: 你可以把检查isDispatching理解为一种约束声明
  3. 为什么要做这种限制 ,因为实际中的 disaptch actions 很多,不知情的情况下就会写出那种循环 dispatch 的代码,虽然限制之后会带来很多的不便

认同的观点

我基(cong)本(lai)不用redux,不过根据问题描述来看,这是为了防止重入。
这和单线程、纯函数没有关系。『之前的 reducer 肯定是已经执行完毕了』只是良好的愿望,实际上可能被违反。而加上这检查就可以在万一玩脱了的时候扔个异常给你冷静一下。
你可以把检查isDispatching理解为一种约束声明(类似于一些语言里的assert断言)。

想从代码层面就杜绝2种情况的出现

  1. 用户在reducer中错误地调用了dispatch,从而再次使reducer被调用,进而造成死循环
  2. 用户在reducer中错误地调用了dispatch,从而再次使reducer被调用,A→B→C→D→E… 不一定死循环,但链路特别长

看代码:如果没有 isDispatching ,用户就有可能写出这样的代码

const reducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      store.dispatch({ type: 'ANY' });
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    case 'ANY':
      store.dispatch({ type: 'INCREMENT' });
      return state;
    default:
      return state;
  }
};

已经作死的调了一下,先手动删除了 isDispatching 的这段控制逻辑 : dispatch({ type: “ANY"}),现象如下: RangeError: Maximum call stack size exceeded

用户在reducer中错误地调用了dispatch,造成死循环

几个关于这个问题的讨论

先看一下这段代码 redux@3.7.2

function dispatch(action) {
  // 无情的省略号...
  if (isDispatching) {
    throw new Error('Reducers may not dispatch actions.');
  }

  try {
    isDispatching = true;
    currentState = currentReducer(currentState, action);
  } finally {
    isDispatching = false;
  }
  // 无情的省略号...
  return action;
}

参考