如何同时调度多个 ngrx 动作
Posted
技术标签:
【中文标题】如何同时调度多个 ngrx 动作【英文标题】:How to dispatch multiple ngrx actions at the same time 【发布时间】:2020-01-25 04:07:24 【问题描述】:我正在使用 ngrx 并且有一个场景,我需要同时调度 2 个动作。我的状态具有更新和更新的属性,如下所示。
//from reducer
const defaultCardState: CardState =
ids: [],
entities: ,
loaded: false,
loading: false,
adding: false,
added: false,
updating: false,
updated: false,
deleting: false,
deleted: false
;
这些是我从组件中调度的操作
this.store.dispatch(fromCard.updateCard(id: id1, changes: name: name1))
this.store.dispatch(fromCard.updateCard(id: id2, changes: name: name2))
下面是我的动作、reducer和效果
//Update Card Actions
export const updateCard = createAction('[Cards] Update Card', props<id: string, changes: any>())
export const updateCardSuccess = createAction('[Cards] Update Card Success', props<changes: any>());
export const updateCardFail = createAction('[Cards] Update Card Fail')
//Reducer
on(fromCards.updateCard, (state) => (...state, updating: true, updated: false)),
on(fromCards.updateCardSuccess, (state, action: any) => (...cardAdapter.updateOne(action.changes, state), updated: true, updating: false)),
on(fromCards.updateCardFail, (state, action: any) => fromCards.updateCardFail),
//Update Card Effect
updateCard$: Observable<Action> = createEffect(() => this.actions$.pipe(
ofType(fromCardActions.updateCard),
map((action: any) => return id: action.id, changes: action.changes),
switchMap((action: any) => this.cardService.updateCard(action).pipe(
map((res) => (fromCardActions.updateCardSuccess(changes: action ))),
catchError(() => of(fromCardActions.updateCardFail))
))
))
依次调度这些操作以使更新和更新字段不冲突的最佳方式是什么?如果我只运行其中一个,它可以工作,但如果我像上图所示将它们一起发送,则只有一个完成。我看到两个动作都被分派了,但只有一个成功动作被分派。
【问题讨论】:
【参考方案1】:你没有,你有一个不同的动作,有效载荷采用一组卡片而不是一张卡片,然后减速器返回使用多张卡片更新的新状态。您的 api 还应该能够接受一个数组,以便您的效果可以发送多个到服务器。
【讨论】:
谢谢!所以我会创建一个接受数组的服务,并对后端进行 n 次调用以进行更改? 让您的 api 接受数组的目的是,您可以在一次调用中批量更新,而不是往返服务器。【参考方案2】:您可以在效果中分派多个操作,我建议您仅在效果中执行此操作
考虑下面的例子
@Effect()
dispatchMultiAction$: Observable<Action> = this.actions$.pipe(
ofType<SomeAction.Dispatch>(someActions.Dispatch),
switchMap(_ =>
of(
new someActions.InitData(),
new someActions.GetData(),
new someActions.LoadData()
)
)
);
【讨论】:
使用mergeMap
这样做更简洁。【参考方案3】:
从技术上讲,使用动作来调度另一个多个动作是非常糟糕的做法。它增加了应用程序的复杂性。一段时间后,您无法调试和理解为什么会分派动作。这段视频 Mike Ryan 用 NgRx 描述了良好的行动卫生: https://youtu.be/JmnsEvoy-gY?t=285
【讨论】:
链接到 YouTube 视频不是一个可接受的答案。评论很好,但这不是答案。至少解释一下视频中的内容,然后链接到视频作为参考。 @AdrianBrand 谢谢你的建议。我改变了答案。我分享了这个链接,因为我认为这是了解 ngrx 中动作架构的非常重要的视频。 谢谢!这段视频实际上非常令人大开眼界。我一直在重用动作,并从中看到了一些陷阱。所以他提到独特的事件动作会创建更多动作,但它们也会创建同样多的效果和减速器,对吗?【参考方案4】:与托尼的回答类似,但使用了正确的运算符:
@Effect()
dispatchMultiAction$: Observable<Action> = this.actions$.pipe(
ofType<SomeAction.Dispatch>(someActions.Dispatch),
mergeMap(_ => [
new someActions.InitData(),
new someActions.GetData(),
new someActions.LoadData()
])
)
);
【讨论】:
您没有使用“of”运算符是否有原因。我不明白为什么它有时用于调度操作,有时不用于。 你可以考虑阅读 learnrxjs。他们对每个运营商都有很好的描述。 谢谢。我想我理解 of 运算符的作用,但我不确定为什么有时动作会像 Tony 上面展示的那样作为 observable 发送,而有时不像你的示例中那样 Tony 使用of([])
的唯一原因是因为 switchMap
只会返回一个 observable。从本质上讲,他所做的是一种返回多个操作的解决方法。但是,使用mergeMap
我们可以正确返回多个操作。【参考方案5】:
类似于 xandermonkey 的答案,但使用正确的运算符:
@Effect()
dispatchMultiAction$: Observable<Action> = this.actions$.pipe(
ofType<SomeAction.Dispatch>(someActions.Dispatch),
concatMap(_ => [
new someActions.InitData(),
new someActions.GetData(),
new someActions.LoadData()
])
)
);
注意 concatMap 和 mergeMap 的区别。因为 concatMap 在前一个完成之前不会订阅下一个 observable,所以延迟 2000ms 的来自源的值将首先发出。与立即订阅内部 observable 的 mergeMap 相比,延迟较小(1000 毫秒)的 observable 将发出,然后是需要 2000 毫秒才能完成的 observable。
【讨论】:
以上是关于如何同时调度多个 ngrx 动作的主要内容,如果未能解决你的问题,请参考以下文章