通过 NgRx 以正确的方式使用订阅



我正在与Angular合作,使用NgRx通过动作和化简器操纵状态。我在这里看到的一个常见代码用于在订阅中调用订阅(我知道这通常是错误的,但我不明白为什么,因为它有效(。这个问题完美地解释了如何处理这个问题,但我的情况 - 我认为 - 略有不同,因为我需要先抓住NgRx商店的一部分。

我目前正在做的是以下内容:

this.store.pipe(
take(1),
select(userProfile) // <-- take data from the NgRx store
).subscribe(currentUser => {
this.loggedUser = currentUser;
this.achievementService.getAchievement(
(achievment: Achievement) =>
achievement.id === currentUser.achId
).subscribe(
response => {
// ... more code
this.categoryService.getCategory(response.id).subscribe(
category=> {
// ... more code
}
);
}
);

如您所见,我需要在下一个订阅中使用每个订阅的结果,但首先我需要获得 NgRx 商店的一部分。您将如何重构这部分代码,使其不会使用多个 Observable.subscribes?另外,您能否解释一下在另一个订阅中调用订阅时可能出现的潜在问题?


溶液

@ngfelixl提出的解决方案有效,但我想花一些时间在接受它之前指定几件事。首先,如果 switchMap 中有多行代码,则必须显式返回可观察量:

switchMap(currentUser => {
this.loggedUser = currentUser;
return this.achievementService.getAchievement(
(achievment: Achievement) =>
achievement.id === currentUser.achId
);
})

其次,您仍然需要最终订阅(这是您将使用的唯一订阅(。下面是完整的代码示例:

this.store.pipe(select(userProfile)).pipe(
switchMap(currentUser => {
this.loggedUser = currentUser;
return this.achievementService.getAchievement(...);
}),
switchMap(achievement => this.categoryService.getCategory(achievement.id))
).subscribe(category => ...)

您可以使用嵌套映射(switchMapconcatMapmergeMap(来处理嵌套的可观察量。您想要的是基于先前操作的操作列表。想象一下链的第一个条目。这是 ngrx 存储中的当前用户配置文件。如果这种情况发生变化,其他一切都应该改变。接下来,它获得成就。这些成就是类别所必需的。在这一点上,我们有一个可观察到的当前用户的成就。

我们可以添加另一个switchMap,以根据另一个 http 请求或其他方式修改可观察的成就。我们现在有一个基于用户及其成就的可观察类别。例如,我们可以使用map运算符修改此类别。

this.store.pipe(select(userProfile)).pipe(
switchMap(currentUser => this.achievementService.getAchievement(...)),
switchMap(achievement => this.categoryService.getCategory(achievement.id)),
map(category => ...)
);

另一种方法有效,但您最终会获得大量订阅(每次用户更改或成就更改时,都会建立新的订阅(,您还需要取消订阅它们或确保它们完成。此外,此方法具有更清晰的语法,因此您可以直接阅读它。此外,您只有一个订阅,其他所有内容都由 RxJS 在内部处理。

最新更新