Home > database >  How to avoid unnecessary API call when working with NgRx
How to avoid unnecessary API call when working with NgRx

Time:04-28

I have an application with 2 menu item; Teams, Players. Whenever I toggle between them, I have to unnecessary api call even though I have the data in store.

Below is the effects file: //auth.effects.ts

loadTeams$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(AuthActions.loadTeams),
      switchMap((action) =>
        this.apiService.teams$.pipe(
          tap(() => console.log('get teams request')),
          map((teams: Team[]) => AuthActions.loadTeamsSuccess({ teams })),
          catchError((error) => {
            console.log('err', error);
            return of(AuthActions.loadFailure({ error }));
          })
        )
      )
    );
  });

Below is the component from which I make api call

teams-list.component.ts

export class TeamsListComponent implements OnInit {
  loading$?: Observable<boolean>;
  error$?: Observable<string>;
  teams$?: Observable<Team[]>;
  retrySubject$ = new BehaviorSubject<boolean>(true);

  constructor(private store: Store<State>) {}

  ngOnInit(): void {
    this.store.dispatch(loadTeams());
    this.teams$ = this.store.select(getTeams);
    this.error$ = this.store.select(getError);
    this.loading$ = this.store.select(getLoading);
  }

  fetchRetry() {
    this.retrySubject$.next(false);
  }
}

This line in ngOnInit cause unnecessary api call. this.teams$ = this.store.select(getTeams);

How can I prevent it so that it makes the api call one time when initializing the app?

CodePudding user response:

loadTeams$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(AuthActions.loadTeams),
      // read the actions$ for an load success event
      withLatestFrom(this.actions$.pipe(ofType(AuthActions.loadTeamsSuccess)),
      // no success event? no teams loaded, so go on
      filter(([loadTeamsAction, loadTeamsSuccessAction]) => !loadTeamsSuccessAction),
      // remove unnecessary action
      map(([loadTeams,]) => loadTeams),
      switchMap((action) =>
        this.apiService.teams$.pipe(
          tap(() => console.log('get teams request')),
          map((teams: Team[]) => AuthActions.loadTeamsSuccess({ teams })),
          catchError((error) => {
            console.log('err', error);
            return of(AuthActions.loadFailure({ error }));
          })
        )
      )
    );
  });

CodePudding user response:

Read the store after receiving the event, then filter out emits that already have a value in the store.

More info: https://timdeschryver.dev/blog/start-using-ngrx-effects-for-this/#enhance-your-action-with-global-store-state

detail = createEffect(() => {
  return this.actions.pipe(
    ofType(ProductDetailPage.loaded),
    concatLatestFrom(() => this.store.select(selectProducts)),
    filter(([{ payload }, products]) => !!products[payload.sku]),
    mergeMap(([{payload}]) => {
      ...
    })
  )
})
  • Related