我应该在哪里放置与 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 退出时通知订阅者,那么对于 C
和 D
.
我们根本不会通知他们
无论如何,这在当前的中间件架构下是不可能的。中间件并不是为了防止订阅者被解雇。
然而,您所描述的可以通过像 redux-batched-subscribe 这样的商店增强器来实现。它与 Redux Thunk 无关,但它会导致任何一组同步调度的动作被去抖动。这样,您会收到一份 A
和 B
的通知,以及另一份 C
和 D
的通知。也就是说,在我看来,依赖编写代码会很脆弱。
(注:我的问题写的不是很清楚,我想的有些不对.)
我想要一个将项目添加到商店并将其注册到外部依赖项的操作。
我可以使用 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))
anddoSomeSideEffect()
.
在 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 退出时通知订阅者,那么对于 C
和 D
.
无论如何,这在当前的中间件架构下是不可能的。中间件并不是为了防止订阅者被解雇。
然而,您所描述的可以通过像 redux-batched-subscribe 这样的商店增强器来实现。它与 Redux Thunk 无关,但它会导致任何一组同步调度的动作被去抖动。这样,您会收到一份 A
和 B
的通知,以及另一份 C
和 D
的通知。也就是说,在我看来,依赖编写代码会很脆弱。