Search code examples
actionngrxdispatch

NgRx how to dispatch 2 actions in order


I cant seem to find a way with the NgRx (not RxJS Style) to dispatch 2 Actions in an effect.

I would like to (IN THIS ORDER):

  1. delete a Movie in the Database with an effect,
  2. dispatch deleteMovieSuccess
  3. dispatch getMovies (I need to reload all Movies afterwards!)

I tried to do it like below, but it just fires the first Action, and I cannot see the other action: In my log I can see:

  • [Movie list] Delete movie
  • [Movie list] Get movies
  • [Movie list] Get movies successful

I have the folloing actions:

export const getMovies = createAction('[Movie List] Get Movies', props<{search: string, page: number, limit: number}>());
export const getMoviesSuccess = createAction('[Movies List] Get Movies Success', props<{ search: string, page: number, limit: number }>());

export const deleteMovie = createAction('[Movie List] Remove Movie', props<{ movieToDelete: Movie, search: string, page: number, limit: number }>());
export const deleteMovieSuccess = createAction('[Movie List] Remove Movie Success', props<{ movieToDelete: Movie }>());

and the following effect:

    deleteMovie$ = createEffect(() => this.actions$.pipe(
      ofType(MovieActions.deleteMovie),
      mergeMap(({movieToDelete, search, page, limit}) =>
        this.moviesService.deleteMovie(movieToDelete)
        .pipe(
          map(() => MovieActions.deleteMovieSuccess({movieToDelete: movieToDelete})),
          map(() => MovieActions.getMovies({search, page, limit})),
          catchError(() => EMPTY)
        )
      )
    )
  );

How can I trigger BOTH, deleteMoviesSuccess, and getMovies in this order?

Ive also tried with switchMap and and flatMap, but never are both Actions dispatched correctly. I just cant seem to understand, how dispatching in an iterative way is possible, but I really need it for my special usecase.

Any help is greatly appreciated.


Solution

  • You should not dispatch two action in as a result of an effect. Rather dispatch some kind of success action and then react on that in other effects. Read here

    You could create a chain of effect:

    1. dispatch delete action
    2. on finish of delete dispatch deleteSuccess action
    3. on deleteSuccess action trigger loadMovies effect
    4. on loadMovies success some set action for a reducer to pick up
    deleteMovie$ = createEffect(() => this.actions$.pipe(
        ofType(MovieActions.deleteMovie),
        mergeMap(({movieToDelete, search, page, limit}) => this.moviesService.deleteMovie(movieToDelete).pipe(
            map(() => MovieActions.deleteMovieSuccess({movieToDelete: movieToDelete})),
            catchError(() => EMPTY)
        ))
    ));
    
    loadMovie$ = createEffect(() => this.actions$.pipe(
        ofType(MovieActions.deleteMovieSuccess),
        mergeMap(() => this.moviesService.loadMovies().pipe(
             map(movies => MovieActions.setMovies({ movies })),
             catchError(() => EMPTY)
        ))
    ));
    

    EDIT
    Also instead of passing parameters like limit or search you may hold these in the store. Doing so gives you the advantage to always access those in effects when needed. The NgRx Documentation has a great example on how this selecting in an effect is done. ngrx.io/api/effects/concatLatestFrom