0

在我的 CRA 项目中,我有一个configureStore.ts导出我的 redux 存储的文件:

// ...
export const store = createStore(
  rootReducer,
  initialState,
  compose(applyMiddleware(...middleware), ...enhancers)
);

我手动订阅了商店staticIntl.ts,在整个项目中被导入了数百次

import { store } from "../configureStore"

class IntlManager  {
  constructor() {
    store.subscribe(this.onStoreChange);
  }
  // ...
}

const manager = new IntlManager()

但是当我运行项目时,store.subscribe失败是由于

TypeError:无法读取未定义的属性“订阅”

我几乎可以肯定这是由于进口订单/可用性

我设法通过无延迟 setTimeout 将订阅回调推送到任务队列的末尾来“解决”这个问题(我知道它实际上舍入到了几个 MS,但我认为我真的不需要介意这一点):

constructor() {
  setTimeout(() => store.subscribe(this.onStoreChange));
}

这安全吗?我主要关心三件事:

  1. 即使包裹在 setTimeout 中,订阅是否仍可能由于相同的原因而失败?例如,如果以某种方式处理我的模块需要很长时间
  2. 是否有可能在订阅之前发生第一次商店更新(因此在订阅回调调用中丢失)?
  3. 由于我的 React 应用程序从服务器获取数据并分派响应,是否会发生这种更新?

我的修复/破解还有其他潜在的陷阱吗?

另外,还有什么其他方法可以解决我的问题?我会特别感谢一个指向正确指南的链接。

4

2 回答 2

2

我几乎可以肯定这是由于进口订单

是的,这听起来很像循环依赖的问题。您configureStore导入了依赖于 的东西staticIntl,而后者又从尚未评估的模块中导入了商店。

不要使用setTimeout,修复你的模块依赖的潜在问题。

特别是,我会推荐

  • 移动const manager = new IntlManager()到另一个文件中,您可以清楚地控制它在createStore(…)调用后执行。
  • 使用依赖反转 -store.subscribe(this.onStoreChange)从构造函数中删除该行IntlManager,而是store.subscribe(manager.onStoreChange)在创建存储后写入。
于 2020-11-16T10:50:10.107 回答
1

我假设IntlManager是一个单例,您的应用程序中只需要其中一个 - 如果这不是真的,请忽略以下内容。

按照经典的 redux 设置https://redux.js.org/recipes/configuring-your-store,我的建议是将商店作为道具传递给您的主 App 组件。IntlManager然后通过将storeprop 传递给构造函数来创建一个实例:

const store = configureStore()

render(
  <Provider store={store}>
    <App store={store} />
  </Provider>,
  document.getElementById('root')
)

App组件中:

const { store } = this.props;
const manager = new IntlManager(store);

这是否有效取决于IntlManager您的应用程序实际使用的方式。您写道,它“在整个项目中被导入了数百次”。我认为这没关系,您可能需要稍微重写该类,以便您可以立即实例化它,但稍后在商店实际可用时订阅商店。我设想这样的事情:

App组件中:

import manager from '.../IntlManager';
// somewhere in componentDidMount for example:
const { store } = this.props;
manager.subscribeToStore(store);
于 2020-11-16T09:05:10.800 回答