Потенциальное состояние гонки с использованием ngrx вложенных switchMaps - PullRequest
0 голосов
/ 16 апреля 2020

У меня есть приложение Angular 9, которое по большей части использует наблюдаемые. В одном компоненте мне нужно:

  1. Получить все компании (так как они содержат определенную информацию, которая мне нужна)
  2. Затем получить все ответы и отобразить некоторую дополнительную информацию в компаниях (т.е. название компании для ответа).
  3. Вернуть этот список в качестве наблюдаемого объекта, на который подписан шаблон, используя трубу asyn c.

Для этого у меня изначально было следующий код (который, кажется, работает):

getPopulatedResponses(): Observable<ResponseModel[]> {
    return this.companyFilesStore
      .select(companyFilesSelector)
      .pipe(
        switchMap(companies => {
          return this.getAccessibleResponses(companies);
        })
      )
  }

getAccessibleResponses(accessibleCompanies: CompanyFilesModel[]): Observable<ResponseModel[]> {
    return this.responsesStore
      .select(responsesSelector)
      .pipe(
        map((responses) => {
          return responses?.map((response) => {
            const company = accessibleCompanies?.find(c => c.companyGuid === response.companyId);
            response.companyName = company?.companyName;
            return response;
          }).sort((a, b) => {
            return a.completedDateTime < b.completedDateTime ? 1 : -1;
          })
        })
      )

Во-первых, я не уверен, что switchMap был правильным оператором, потому что, если companyFilesSelector обновлялся, а затем responseSelector частично запускался, это не заканчивалось бы предыдущая подписка?

И теперь мне нужно также добавить подписку на имеющуюся у меня службу фильтрации, поэтому я внес следующие изменения в метод getAccessibleResponses (который опять-таки работает, но мне кажется, что это так). может быть больше благодаря удаче / хорошей связи, чем что-либо еще):

getAccessibleResponses(
    accessibleCompanies: CompanyFilesModel[],
  ): Observable<ResponseModel[]> {
    return this.searchAndFilterService.getFilter()
      .pipe(
        switchMap(filter => {
          return this.responsesStore
            .select(responsesSelector)
            .pipe(
              map((responses) => {
                return responses?.map((response) => {
                  const company = accessibleCompanies?.find(c => c.companyGuid === response.companyId);
                  response.companyName = company?.companyName;
                  return response;
                })
                  ?.filter(r => !r.isArchived)
                  ?.filter(r => !filter?.selectedCompany || r.companyId === filter.selectedCompany.companyId)
                  ?.filter(r => !filter.selectedAssessmentTemplate ||
                    r.assessmentTemplateId === filter.selectedAssessmentTemplate.assessmentTemplateId)
                  .sort((a, b) => {
                    return a.completedDateTime < b.completedDateTime ? 1 : -1;
                  })
              })
            )
        })
      )
  }

Я вполне уверен, что это не самый лучший или даже правильный подход, но я немного теряюсь и пытаюсь понять, как добиться следующего:

  1. Получите все компании в первую очередь.
  2. Объедините результаты компаний, наблюдаемых с ответы.
  3. При изменении фильтра (т. е. наблюдаемой службы фильтрации) фильтруйте результаты 1 / 2.

Любая помощь или руководство будет с благодарностью.

1 Ответ

1 голос
/ 17 апреля 2020

Насколько я могу судить, у вас есть 3 основные части для работы (companies, responses и filter), и когда одна из этих частей изменяется (например, выдает новое значение), вы хотели бы пересчитать данные, основываясь на том, что изменилось.

Если это так, и предположить, что состояние вашего приложения поддерживается в единственном источнике правды , вот как я бы это сделал:

const companies$ = this.store.select(companyFilesSelector);

const accessibleResponses$ = this.store.select(responsesSelector);

const activeFilter$ = this.store.select(activeFilterSelector);

const data$ = combineLatest(
  companies$,
  accessibleResponses$,
  activeFilter$,
).pipe(
  map(([companies, responses, filter]) => {
    // Logic here...
  })
)

combineLatest используется здесь, потому что вы хотите вычислять показанные данные всякий раз, когда изменяется одна из 3 частей.

Итак, если ваше начальное состояние может быть:

{
 companies: [],
 responses: [],
 activeFitler: null,
}

Каждый раз, когда вы добавляете, например, некоторые новые компании, функция, предоставляемая для map, будет вызываться для обновления того, что будет показано пользователю в зависимости от новых компаний. То же самое происходит, когда вы добавляете новый responses или новый filter.


Редактировать

Могу ли я использовать SwitchMap из этого, чтобы никакая подписка не смотрела в этом магазине потенциально сэкономить память?

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

Кроме того, поскольку вы подписываетесь на хранилище, первое получение данных должно происходить синхронно , поскольку я полагаю, что хранилище является своего рода BehaviorSubject (например, NgRx)

...