Angular / NGRX - предотвращать дублирование запросов с одинаковыми параметрами - PullRequest
0 голосов
/ 25 марта 2020

Предположим, у меня есть компонент, который отображает список актеров из 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}).

1 Ответ

0 голосов
/ 25 марта 2020

С помощью вашего решения 2 вы можете добавить еще одну проверку уникального идентификатора с помощью distinct()

getActors$ = createEffect(() => this.actions$.pipe(
    ofType(getActors),
    withLatestFrom(action =>
        of(action).pipe(
            this.store.pipe(select(selectMovieActors(movieId: {action.movieId})))
        )
    ),
    filter(([{payload}, actors]) => !!actors
    distinct(payload => payload.movieId)
    mergeMap((payload) => ...
...