我应该在哪里放置与 redux 中的动作相关的同步副作用?

Where should I put synchronous side effects linked to actions in redux?

(注:我的问题写的不是很清楚,我想的有些不对.)

我想要一个将项目添加到商店并将其注册到外部依赖项的操作。

我可以使用 thunk 中间件并编写

export function addItem(item) {
  return dispatch => {
    dispatch(_addItemWithoutRegisteringIt(item));
    externalDependency.register(item);
  };
}

但订阅者会在项目注册之前收到通知,他们可能依赖于项目是否已注册。

我可以颠倒顺序写

export function addItem(item) {
  return dispatch => {
    externalDependency.register(item);
    dispatch(_addItemWithoutRegisteringIt(item));
  };
}

但是我通过一个唯一的 id 跟踪外部依赖项中的项目,它很自然地只在 reducer 中分配。

我可以在 reducer 中注册该项目,但我了解到在 reducer 中产生副作用是非常糟糕的形式,可能会导致后续问题。

那么最好的方法是什么?

(我的结论是:有多种方法可行,但对于我的用例来说最好的方法可能是将句柄存储到 Redux 的外部依赖项中,而不是将句柄存储到外部依赖项中的 Redux .)

我还在学习 Redux 的过程中;然而我的直觉告诉我这可能是某些 custom middleware?

的潜在候选者

如果使用Redux Thunk中间件,可以将其封装在action creator中:

function addItem(id) {
  return { type: 'ADD_ITEM', id };
}

function showNotification(text) {
  return { type: 'SHOW_NOTIFICATION', text };
}

export function addItemWithNotification(id) {
  return dispatch => {
    dispatch(addItem(id));
    doSomeSideEffect();
    dispatch(showNotification('Item was added.');
  };
}

根据对此答案的评论进行详细说明:

Then maybe this is the wrong pattern for my case. I don't want subscribers invoked between dispatch(addItem(id)) and doSomeSideEffect().

在 95% 的情况下,您不必担心订阅者是否被调用。如果数据没有改变,像 React Redux 这样的绑定将不会重新渲染。

Would putting doSomeSideEffect() in the reducer be an acceptable approach or does it have hidden pitfalls?

不,将副作用放入 reducer 是永远不能接受的。 这违背了 Redux 的中心前提,并且破坏了其生态系统中的几乎所有工具:Redux DevTools, Redux Undo 、任何 record/replay 解决方案、测试等。永远不要这样做。

如果您确实需要与一个动作一起执行副作用,并且您也真的关心订阅者只被通知一次,只需分派一个动作并使用 [Redux Thunk ] 给它“附加”一个副作用:

function addItem(id, item) {
  return { type: 'ADD_ITEM', id, item };
}

export function addItemWithSomeSideEffect(id) {
  return dispatch => {
    let item = doSomeSideEffect(); // note: you can use return value
    dispatch(addItem(id, item));
  };
}

在这种情况下,您需要处理来自不同减速器的 ADD_ITEM。没有必要在不通知订阅者两次的情况下分派两个动作。

Here is the one point I still definitely don't understand. Dan suggested that the thunk middleware couldn't defer subscriber notification because that would break a common use case with async requests. I still don't understand this this.

考虑一下:

export function doSomethinAsync() {
  return dispatch => {
    dispatch({ type: 'A' });
    dispatch({ type: 'B' });
    setTimeout(() => {
      dispatch({ type: 'C' });
      dispatch({ type: 'D' });
    }, 1000);
  };
}

您希望何时通知订阅?当然,如果我们只在 thunk 退出时通知订阅者,那么对于 CD.

我们根本不会通知他们

无论如何,这在当前的中间件架构下是不可能的。中间件并不是为了防止订阅者被解雇。

然而,您所描述的可以通过像 redux-batched-subscribe 这样的商店增强器来实现。它与 Redux Thunk 无关,但它会导致任何一组同步调度的动作被去抖动。这样,您会收到一份 AB 的通知,以及另一份 CD 的通知。也就是说,在我看来,依赖编写代码会很脆弱。