【问题标题】:nx react redux store - circular dependciesnx react redux store - 循环依赖
【发布时间】:2021-05-07 03:27:49
【问题描述】:

如何用 react 和 redux store 解决 nx 工作空间中的循环依赖问题?

例如,如果我有一个包含所有存储配置的库,则有一个文件 createRootReducer - 它结合了我的减速器:

import {
  someSliceReducer,
  SOME_SLICE_FEATURE_KEY,
} from '@app/feature';

export const createRootReducer = (history: History) =>
  combineReducers({
    router: connectRouter(history),
    form: formReducer,
    [SOME_SLICE_FEATURE_KEY]: someSliceReducer,
  });

存储库中的应用程序状态为:

import { createRootReducer } from './createRootReducer';
export type ApplicationState = ReturnType<ReturnType<typeof createRootReducer>>;

问题来自于在 someSliceReducer 所在的库中导入应用程序状态:

import {ApplicationState} from '@app/store'
const entities= useSelector(
    (state: ApplicationState) => state.dischargeFolder.loadingStatus
  );

现在我们有了一个循环依赖,nx 警告我们:

Circular dependency between "feature" and "store" detected: feature -> store -> feature eslint@nrwl/nx/enforce-module-boundaries

如何防止这种情况发生?我需要在 root-reducer 中导入 reducer,但我还需要将 ApplicationState 导入到功能中。 我试图将 root-reducer 提取到自己的库中 - 但这并不能解决 nx 抱怨超过 3 个库的循环依赖的问题。

我还尝试使用“reducerRegistry”,它接受一个 reducer 并在内部注册它 - 不幸的是,使用这种方法我丢失了已注册 reducer 的类型信息,并且 ApplicationState 没有被正确推断。

【问题讨论】:

  • 也许this ticket 需要更多人参与。我也遇到过类似问题。

标签: reactjs typescript redux nomachine-nx


【解决方案1】:

如果您的选择器只与一个切片相关,那么它们不需要了解整个状态。他们只需要知道切片状态的类型,并知道从整个应用程序状态中提取该状态。

使用文档中的反例,关键是如果我们将所有切片添加为顶级减速器(非嵌套),那么我们知道应用程序状态满足 {counter: CounterState} 并且我们不关心任何其他切片或者他们的状态是什么。

我使用了一些模式来实现这一点。目前我最喜欢的是为特定切片定义选择器,以便它们只从自己的切片状态中进行选择。显然你不能用那个选择器调用useSelector,所以我为每个切片创建了一个自定义钩子。该钩子实际上可以存在于该切片的文件夹中。

export const useCounterSelector = <T>(selector: (state: CounterState) => T): T => 
  useSelector(({counter}: {counter: CounterState}) => selector(counter))

如果您要处理很多切片,您可以制作一个通用版本。在这里,我将切片本身作为参数传递,以获取名称(假定为它在主减速器中的键)和状态类型。

export const createUseSelector = <Name extends string, SliceState>(slice: Slice<SliceState, any, Name>) => 
  <T>(selector: (state: SliceState) => T): T => 
    useSelector((state: {[K in Name]: SliceState}) => selector(state[slice.name]))

export const useCounterSelector = createUseSelector(counterSlice);
// has inferred type <T>(selector: (state: CounterState) => T) => T

【讨论】:

  • 非常感谢您的回答,它让我朝着正确的方向前进。不幸的是,它不仅是导致问题的 ApplicationState - 我更正了这一点,只使用了真正需要的状态部分。不幸的是,我还必须在我的许多 unitTests 或 Storybook 文件中从“@app/store”库中导入 configureStore。这再次导致循环依赖。 libaray -> 存储 -> 库
猜你喜欢
  • 1970-01-01
  • 2019-10-17
  • 2021-05-23
  • 1970-01-01
  • 2022-01-09
  • 2019-03-10
  • 1970-01-01
  • 2010-09-12
相关资源
最近更新 更多