NgRx как отправить 2 действия по порядку

#action #ngrx #dispatch

Вопрос:

Кажется, я не могу найти способ с помощью NgRx (не в стиле RxJS) отправить 2 действия в эффекте.

Я хотел бы (В ТАКОМ ПОРЯДКЕ):

  1. удалите фильм из базы данных с эффектом,
  2. процесс удаления сообщений об отправке
  3. отправка getMovies (мне нужно перезагрузить все фильмы после этого!)

Я попытался сделать это, как показано ниже, но это просто запускает первое действие, и я не вижу другого действия: в моем журнале я вижу:

  • [Список фильмов] Удалить фильм
  • [Список фильмов] Получить фильмы
  • [Список фильмов] Сделайте фильмы успешными

У меня есть следующие действия:

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

и следующий эффект:

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

Как я могу запустить И deleteMoviesSuccess, и getMovies в таком порядке?

Я также пробовал использовать switchMap и faltMap, но оба действия никогда не выполняются правильно. Я просто не могу понять, как возможна отправка итеративным способом, но мне это действительно нужно для моего специального использования.

Мы будем очень признательны за любую помощь.

Ответ №1:

Вы не должны отправлять два действия в результате эффекта. Скорее направьте какое-то успешное действие, а затем отреагируйте на это другими эффектами. Читайте здесь

Вы могли бы создать цепочку эффектов:

  1. действие по удалению отправки
  2. по завершении действия удалить отправку удалить процесс
  3. при удалении срабатывает триггер loadMoviesAction
  4. на loadMovies успех какое-то установленное действие для редуктора, чтобы забрать
 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)
    ))
));
 

Редактировать
Кроме того, вместо передачи параметров, таких как ограничение или поиск, вы можете хранить их в магазине. Это дает вам преимущество всегда иметь доступ к тем, кто действует, когда это необходимо. В документации NgRx есть отличный пример того, как выполняется этот выбор в эффекте. ngrx.io/api/effects/concatLatestFrom

Комментарии:

1. Мне нравится этот подход, так как его легко понять и следовать, но как бы я передал аргументы от первого эффекта ко второму? Я должен передать {поиск, страница, ограничение} второму эффекту, так как я должен вызвать getMovies() с этими точными параметрами. Должен ли я просто передать их в действие deleteMovieSuccess? будут ли они доступны во втором эффекте?

2. Передача его в процессе удаления, безусловно, возможна, но я бы предпочел, чтобы эти параметры были в магазине, и вместо того, чтобы отправлять их с эффектом, вы просто выбираете из магазина. ngrx.io/api/effects/concatLatestFrom

3. Не могли бы вы включить это в свой ответ, чтобы будущие читатели могли его найти? Тогда я думаю, что это общепринятый путь… Спасибо!

4. Сделано! Также посмотрите на этот линтер NgRx. Я думаю, что это действительно помогает поддерживать код в чистоте

Ответ №2:

вы можете использовать switchMap оператор для возврата массива действий.

Например, вместо;

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

Вы могли бы попробовать;

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

Комментарии:

1. Будет ли это следовать точному порядку, в котором выполняются Действия? Если это так, то я думаю, что это хорошее решение

2. Вы не должны вложенно переключать/объединять карты. Скорее посмотрите на эффект цепочки. Таким образом, весь код становится более читаемым и доступным для обслуживания.

3. @DigitalJedi Поскольку это массив, любые переданные действия в массиве будут запускаться в том же порядке, что и индекс массива.