#action #ngrx #dispatch
Вопрос:
Кажется, я не могу найти способ с помощью NgRx (не в стиле RxJS) отправить 2 действия в эффекте.
Я хотел бы (В ТАКОМ ПОРЯДКЕ):
- удалите фильм из базы данных с эффектом,
- процесс удаления сообщений об отправке
- отправка 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:
Вы не должны отправлять два действия в результате эффекта. Скорее направьте какое-то успешное действие, а затем отреагируйте на это другими эффектами. Читайте здесь
Вы могли бы создать цепочку эффектов:
- действие по удалению отправки
- по завершении действия удалить отправку удалить процесс
- при удалении срабатывает триггер loadMoviesAction
- на 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 Поскольку это массив, любые переданные действия в массиве будут запускаться в том же порядке, что и индекс массива.