如何修复切片与 RootState 的循环依赖?

How to fix circular dependencies of slices with the RootState?

我最近开始使用 redux-toolkit 并开始使用 createSlice 按照他们的文档编写我的减速器。

一个reducer,我们称它为reducerA,导入customAsyncFunction来处理它的回调,这个函数是通过createAsyncThunk创建的,它又在它时读取RootState调用 thunkApi.getState(),现在的问题是当导入 RootReducer 时,将导入 reducerA 生成循环引用。

基本上:RootReducer -> reducerA -> actions -> RootReducer -> ...

下面我尝试简化问题。

// actions.ts file
import { RootState } from "./RootReducer";

export const customAsyncAction = createAsyncAction("myaction", async (_, thunkApi) =>
  const state = thinkApi.getState() as RootState;
  ...
);


// reducerA.ts file
import { customAsyncAction } from "./actions";

const slice = createSlice({
  ...
  extraReducers: {
    [customAsyncAction.fulfilled.toString()]: ... // handles fulfilled action
  }
});

export default slice.reducer;



// RootReducer.ts file
import reducerA from "./reducerA"
import reducerB from "./reducerB"

const reducers = combineReducers({
  reducerA,
  reducerB
});

export type RootState = ReturnType<typeof reducers>; // complains about circular reference

this section of the documentation 中提到了这种情况发生的可能性,并且模糊地建议将代码拆分到文件中。然而,从我所有的尝试中,我似乎无法找到解决这个问题的方法。

Type-only 循环引用没问题。 TS 编译器将在编译时解决这些问题。特别是,切片文件导出其缩减器,将缩减器导入商店设置,根据该切片定义 RootState 类型,然后 re-import 返回 RootState 类型是正常的放入切片文件。

只有在涉及运行时行为时,循环导入才是一个潜在的问题,例如两个切片依赖于彼此的操作。

不幸的是,据我所知,用于捕获循环依赖项的 ESLint 规则无法判断正在导入的内容只是一种类型。

在阅读了关于如何将 createAsyncThunk 与 TypeScript 结合使用的 following docs section 之后,我将 extraReducers 的实现更改为使用 builder 模式而不是传递一个键值对象和错误消失。

// before
const slice = createSlice({
  ...
  extraReducers: {
    [customAsyncAction.fulfilled.toString()]: ... // handles fulfilled action
  }
});

// after
const slice = createSlice({
  ...
  extraReducers: builder => {
    builder.addCase(customAsyncAction.fulfilled, (state, action) => ...)
  }
});

我必须承认,我无法准确指出为什么在第一种情况下它不起作用,而在第二种情况下却起作用。

Babel 认为 import 是一个模块,并不知道它是一个类型,完全可以导入它。为了告诉它是一个类型导入尝试将它作为一个类型导入:

import type { RootState } from "./RootReducer";

注意 import 之后的 type 关键字。这样 babel/eslint 知道您导入的是类型,而不是模块,并将其从依赖映射中排除,从而解决问题。