【问题标题】:Using subscribe in the correct way with NgRx在 NgRx 中以正确的方式使用订阅
【发布时间】:2020-01-05 09:48:28
【问题描述】:

我正在一个使用 Angular 的项目中工作,使用 NgRx 通过动作和减速器来操纵状态。我在这里看到的一个通用代码用于在订阅中调用订阅(我知道这通常是错误的,但我不明白为什么,因为它有效)。 This question 完美地解释了如何处理这个问题,但我的情况 - 我认为 - 略有不同,因为我需要先抢一块 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.subscribe?另外,你能解释一下在另一个内部调用订阅时可能出现的潜在问题吗?


解决方案

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

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 => ...)

【问题讨论】:

  • switchMap 总是希望您返回一个 observable,因为您切换了流。在一行中,关键字 return 被省略了。当使用 observables 时,你总是会有订阅,因为没有订阅者,生产者是无用的。当您遇到将一个 observable 的输出用作第二个 observable 的输入的问题时,SwitchMap 通常是首选解决方案。
  • 不客气,编码愉快 :-)

标签: angular typescript ngrx


【解决方案1】:

您可以使用嵌套映射(switchMapconcatMapmergeMap)来处理嵌套的 observables。您想要的是基于先前操作的操作列表。想象一下链的第一个条目。这是您的 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 内部处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-19
    • 1970-01-01
    • 2020-08-20
    • 1970-01-01
    • 1970-01-01
    • 2019-11-27
    • 1970-01-01
    相关资源
    最近更新 更多