当我在获取数据后调度操作时无限循环 w/subscribe

infinite loop when i dispatch action after getting data w/subscribe

我是 angular 6 和 ngrx 商店的新人。我尝试在从商店获取数据订阅后发送操作,但它会造成无限循环并使浏览器崩溃?我错了什么。一些解决方案我发现它使用 rxjs 的 do/tap 运算符但仍然无法正常工作。例如,当我使用 {{(feedState | async).loading}} 时,它总是 return undefined .

我的组件:

  ngOnInit() {
    this.store.dispatch(new FeedActions.GetFeedCategories());
    this.feedSubscription = this.store
      .pipe(
        select('feed'),
        map(data => {
          this.feedState = data;
          return data.categories;
        }),
        tap(data =>
          this.store.dispatch(
            new FeedActions.GetFeedItems({
              cat_id: data[this.selectedIndex],
              page: 0
            })
          )
        )
      )
      .subscribe(data => {});
  }

select 运算符将创建一个可观察对象,它会在每次更新 'feed' 的状态时发出。这将在您执行 FeedActions.GetFeedCategories() 时第一次触发,但当 FeedActions.GetFeedItems(...) 的结果添加到状态时它也会再次触发,这将导致 FeedActions.GetFeedItmes(...) 再次执行,并且一次又一次...

简单的解决方案是在管道中添加一个 take(1),这样您就只会获得地图和点击运算符的一次点火:

ngOnInit() {
    this.store.dispatch(new FeedActions.GetFeedCategories());
    this.feedSubscription = this.store
      .pipe(
        select('feed'),
        take(1),
        map(data => {
          this.feedState = data;
          return data.categories;
        }),
        tap(data =>
          this.store.dispatch(
            new FeedActions.GetFeedItems({
              cat_id: data[this.selectedIndex],
              page: 0
            })
          )
        )
      )
      .subscribe(data => {});
  }

但是,可能值得考虑在此处拆分问题 - 您已将准备状态的工作与 select 显示状态的工作混合在一起。更好的解决方案可能是这样的:

ngOnInit() {
    this.store.dispatch(new FeedActions.GetFeedCategories());
    this.store.pipe(
        select('feed'),
        take(1),
        map(data => data.categories),
        tap(data =>
          this.store.dispatch(
            new FeedActions.GetFeedItems({
              cat_id: data[this.selectedIndex],
              page: 0
            })
          )
        )
      )
      .subscribe(() => {});

      this.feedState = this.store.pipe(
          select('feed')
      );
  }

... 然后在您的模板中,您可以根据需要使用 {{feedState | async}}?.loading 或任何内容。

async 管道为您进行订阅,并需要一个可观察的,而不是原始数据字段。在您的示例中,this.feedState 应该是 Observable<FeedState> 类型,但从提供的代码看来它是原始数据类型(例如 FeedState 而不是 Observable)。