Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

i'm new in angular 6 and ngrx store. I try to dispatch action after get data subscribe from store but it make infinite loop and crash browser? What i was wrong . Some solution i find it using do/tap operator of rxjs but still not working. And when i use {{(feedState | async).loading}} for example , it alway return undefined .

my component:

  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 => {});
  }
like image 667
duy khanh Avatar asked Aug 29 '18 08:08

duy khanh


1 Answers

The select operator will create an observable which emits every time the state of 'feed' is updated. This will fire the first time when you do your FeedActions.GetFeedCategories() but it will also fire again when the result of FeedActions.GetFeedItems(...) is added to the state, which will cause FeedActions.GetFeedItmes(...) to be executed again, and again, and again...

The simple solution is to add a take(1) into the pipe, so you only get a single fire of the map and tap operators:

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 => {});
  }

However, it may be worth considering splitting the concerns here - you've mixed the job of preparing the state with the job of selecting the state for display. A better solution may be something like this:

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')
      );
  }

... then in your template, you can use {{feedState | async}}?.loading or whatever as needed.

The async pipe does the subscription for you and expects an observable, not a raw data field. In your example, this.feedState should be of type Observable<FeedState>, but it looks to be a raw data type (e.g. FeedState instead of Observable) from the code provided.

like image 200
Mark Hughes Avatar answered Nov 20 '22 22:11

Mark Hughes