避免了Redux中多个reducer监听相同动作的导入顺序/循环依赖错误

Avoiding import order/cirulcar dependency bugs with multiple reducers listening to same action in Redux

本文关键字:导入 顺序 错误 依赖 循环 Redux 监听 reducer      更新时间:2023-09-26

我一直在一个Redux项目中工作,使用一种似乎很常见的"模块"模式,其中操作集、操作创建者和负责单个状态片的操作处理程序被组合在一个文件中,从这些文件导出的reducer被combineReducers合并到一个根reducer中。

我一直在使用这种模式取得了一些成功,但是当我试图让模块听其他模块导出的动作时,我遇到了一些非常微妙和令人讨厌的错误。

我第一次遇到这个问题时,是模块a从模块B导入一个动作,模块B从模块a导入一个动作,形成了一个循环依赖。很好,可以理解,我可以接受处理这种情况。

但是我也遇到过这样的情况,即简单地从模块B中导入模块A的操作导致该操作未定义-我假设基于模块被combineReducers导入或消化的顺序,尽管我还没有确切地计算出如何(即使模块A在rootReducer文件中先于模块B导入,有时似乎也会发生)。

所以我的问题是:在多个reducer中听相同的动作被认为是一个好的模式,还是要避免的东西?这似乎是Redux做事方式所鼓励的,但我可能遗漏了一些东西。

举一个人为的例子,假设我想保留应用程序中某些操作的运行日志。所以我有一个"日志"模块,监听所需的操作,并在适当的时候更新其状态片。

假设我有一个用户模块,它开始于:

export const ADD_USER = 'ADD_USER';
export function addUser (user) {
  return {
    type: ADD_USER,
    user: user
  }
};

等。

如果我想在添加新用户时进行日志记录,我可以这样做:

   import { ADD_USER } from './users';
   const ACTION_HANDLERS = {
    [ADD_USER]: (state, action) => {
       return [
         ...state,
         'Added user: ' + action.user.name
       ];
     }
   }
   const initialState = []
   export default function logReducer (state = initialState, action) {
      const handler = ACTION_HANDLERS[action.type]
      return handler ? handler(state, action) : state
   }

大多数情况下,这种方法工作得很好。但有时ADD_USER在导入时是未定义的,因此操作处理程序将(悄悄地)错过该操作。很烦人!

如果这确实是要走的路,我应该如何避免这些bug ?显而易见的解决方案似乎是将动作集合放在单个actions.js文件中,该文件在所有模块之前导入,但这似乎破坏了模块化的意义。另一种选择是只编写动作处理程序,如:

ADD_USER: (state, action) => {}

在听"外部"动作时。但是为什么要将它们定义和导出为常量呢?

是的,Redux绝对鼓励使用多个reducer来响应相同的操作。这就是为什么典型的基本Redux文件结构确实会有单独的文件用于操作常量——它们可以被多个单独的reducer文件导入,也可能被单独的action creator文件导入。

"鸭子"或"模块"结构比较流行的,当然是组织代码的有效方法,但也基于只有一组reducer会响应给定操作的想法。虽然"ducks"规范确实建议从一个模块导出动作常量,但这意味着你实际上不会有这些相互依赖,因为没有其他模块会/应该关心另一个模块中发生了什么。

对于它的价值,Dan Abramov, Redux的创造者,是一个非常支持任意简化器处理相同的动作,并且通常不同意"鸭子"的方法。显然,这只是一个意见,您完全可以随心所欲地为自己的应用程序做任何实际最有效的事情,但这是需要考虑的。

最终,我认为答案是您试图使用"模块化"方法,但发现所需的行为实际上根本没有"模块化"。