Search code examples
angularngrxngrx-storengrx-effects

Bad practice to use store in effect?


I have an InitAction with some params like a profile id and some more data and that action can be called a couple of times with different params.

Besides there is a LoadProfileAction and I added an effect that is listening to InitAction and triggers the LoadProfileAction.

The problem is, that I only want to trigger the LoadProfileAction, if the profile id had changed compared to the previous one so I think the best(?) solution is to use withLatestFrom with the store in my effect and check the current profile id.

But it feels wrong to use the store in an effect because it seems to break the flux principle or isn't that true? Are there any better solutions? Thanks!


Solution

  • Update

    Now NgRX (v12) provides its own operator for using the store inside an effect. You don't need the two concatMap, of, withLatestFrom solution anymore. Now you can use concatMapFrom from the @ngrx/effects library like follows:

    import { concatLatestFrom } from '@ngrx/effects';
    
    ...
    
    createEffect(() => this.actions$.pipe(
      ofType(getSelectedBookDetails),
      concatLatestFrom(action => this.store.select(getSelectedBookId)),
      switchMap(([action, selectedBookId]) => this.bookService.getDetails(selectedBookId).pipe(
        map(...),
        catchError(...)
      )
    );
    

    Old answer from 2020/08/19

    Bit late to answer this, but maybe it is a help for someone out there. I use NgRX almost everyday and I'd like to share my personal insights.

    You can definitely use the store in your effects. You just need to import the store via the constructor (since it is part of the dependency injection tree) and then you can use it in the related effects with the withLatestFrom RxJS operator. If you read the official docs you will probably see a note on the effects documentation which says

    Note: For performance reasons, use a flattening operator in combination with withLatestFrom to prevent the selector from firing until the correct action is dispatched.

    So ideally you use the store in the following way:

    createEffect(() => this.actions$.pipe(
      ofType(getSelectedBookDetails),
      concatMap(action => of(action).pipe(
        withLatestFrom(this.store.select(getSelectedBookId))
      )),
      switchMap(([action, selectedBookId]) => this.bookService.getDetails(selectedBookId).pipe(
        map(...),
        catchError(...)
      )
    );
    

    Where getDetails() is an Angular http request and returns an Observable and getSelectedBookId is an NgRX selector. At least I hope you can get the idea how to use it and that it is not a problem at all to use it in an effect.

    You can also be sure that the effect is executed after the reducer has been executed. This order is guaranteed.