Предположим, у меня есть компонент, который отображает список актеров из mov ie. Компонент отправляет действие при инициализации, чтобы получить список актеров.
actors.component.ts
@Input() filter; // some filter applied to the data, like display only male actors for example
ngOnInit() {
this.store.dispatch(getActors({movieId: this.movieId}));
}
Как видите, у этого компонента есть фильтр, который будет применяться к данным.
URL-адрес API выглядит как-то так: / movies / movieId / актеры
Теперь я могу иметь несколько компонентов на одной странице. Для простоты предположим, что у нас есть 3 компонента: C1, C2 и C3.
C1 и C2 оба нуждаются в данных из одной и той же конечной точки: / movies / 1 / актеры, но каждый компонент фильтрует данные в своем путь. C1 показывает только актеров мужского пола, а C2 - только актеров женского пола.
C3 делает запрос к / movies / 2 / актерам (как вы можете видеть, у него другой идентификатор фильма по сравнению с C1 и C2).
Проблема в том, что в этом случае будет выполнено 3 запроса:
/movies/1/actors
/movies/1/actors
/movies/2/actors
Как видите, дублируется один запрос, и это проблема. В конце я хочу сделать только 2 запроса:
/movies/1/actors
/movies/2/actors
Это соответствующий класс эффектов:
actors.effects.ts
getActors$ = createEffect(() => this.actions$.pipe(
ofType(getActors),
mergeMap((payload) => this.actorsService.getAll({movieId: payload.movieId})
.pipe(
map(actors => ({ type: '[Actors API] Actors Loaded Success', payload: actors })),
catchError(() => EMPTY)
))
)
);
Также существует редуктор, который сохраняет данные в хранилище в следующая форма:
{
movieActors: [
{movieId: 1: actors: [...]}
]
}
Опять же, проблема заключается в том, как предотвратить повторение одного и того же запроса дважды.
Одним из решений является проверка хранилища, если данные уже присутствуют. Таким образом, класс эффектов будет выглядеть примерно так:
getActors$ = createEffect(() => this.actions$.pipe(
ofType(getActors),
withLatestFrom(action =>
of(action).pipe(
this.store.pipe(select(selectMovieActors(movieId: {action.movieId})))
)
),
filter(([{payload}, actors]) => !!actors
mergeMap((payload) => ...
Из приведенного выше примера C1 и C2 примерно одновременно отправят действие getActors. Эффект будет запущен для C1 и сделает запрос, затем тот же эффект будет запущен для C2 немедленно, и будет сделан новый запрос, потому что данные из первого запроса не поступили. Вот почему я не могу использовать это решение.
Другое решение заключается в использовании выхлопной карты:
getActors$ = createEffect(() => this.actions$.pipe(
ofType(getActors),
exhaustMap((payload) => this.actorsService.getAll({movieId: payload.movieId})
.pipe(
map(actors => ({ type: '[Actors API] Actors Loaded Success', payload: actors })),
catchError(() => EMPTY)
))
)
);
Если бы я использовал это решение, то был сделан только один запрос: / кино / 1 / актеры. Второй запрос: / movies / 2 / актеры будут отброшены.
Я искал решения, но, похоже, они не относятся к моему конкретному случаю. Я хочу предотвратить запрос, только если был сделан другой запрос, но с тем же параметром (movieId) в моем случае. Если был запрос к той же конечной точке, но с другим идентификатором, то должен быть сделан новый запрос.
Редактировать Забыл упомянуть, у каждого компонента есть кнопка refre sh который снова отправит действие getActors ({movieId: this.movieId}).