Как извлечь возвращаемое значение из диспетчерского хранилища RxJS

#angular #typescript #rxjs #ngrx

Вопрос:

Короче говоря, я младший разработчик .Net, и я получил задание изменить что-то в нашем приложении Angular, я понятия не имею, как это работает, и я изо всех сил пытаюсь это изменить. Я застрял на одной теме.

Пользователь создается на основе формы, после ее заполнения происходит вызов:

 submit() {
    if (this.accountForm.invalid) {
      return;
    }
    const {
      email,
      accountExpirationDate,
      firstName,
      lastName,
      permissionLevel,
      timezone,
      language,
    } = this.accountForm.value;

    const accountExpirationDateMs = new Date(accountExpirationDate || null).getTime();

    const result = {
      email,
      accountExpirationDate: accountExpirationDateMs || null,
      name: firstName,
      lastName,
      roleExternalIds: [permissionLevel],
      timezone,
      languageExternalId: language,
      avatar: this.avatar
    };

    if (!this.id) {
      this.store.dispatch(fromUser.createUserAction({ result } as any));
    } else {
      this.isPending = true;
      this.store.dispatch(fromUser.editUserAction({ result, id: this.id } as any));
      setTimeout(() => {
        this.store.dispatch(fromSettings.getCurrentUserInfoAction());
      }, 1200);
    }
  }
 

После this.store.dispatch(fromUser.createUserAction({ result } as any)); инструкции if я должен сделать еще два вызова, которые требуют, чтобы я предоставил идентификатор пользователя, который возвращается бэкэндом после создания пользователя, но я понятия не имею, как извлечь его из этого вызова.

Действие Создать пользователя выглядит следующим образом:

 export const createUserAction = createAction('[User management] Create User', props<{result: UserRegistrationRequestModel}>());
 

И я верю, что он делает этот звонок:

   createUser$ = createEffect(() =>
    this.actions$.pipe(
      ofType(fromUserManagement.createUserAction),
      mergeMap((action) => {
        return this.identityServiceClient.create(action.result).pipe(
          map(response => {
            this.messageService.open('User has been created!', 'success');
            this.router.navigateByUrl('/app/users/list');
            return fromUserManagement.createUserActionSuccess(response)
          }),
          catchError(() => {
            this.messageService.open('Something went wrong!', 'danger');
            return fromUserManagement.createUserActionError;
          })
        );
      })
    )
  )
 

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

Ответ №1:

Вы можете dispatch создать новое действие из своего createUser$ эффекта, чтобы выполнить дополнительную работу, и передать в нем необходимые параметры. Затем вы можете создать новый effect для обработки новых отправленных action и вызывать все, что захотите внутри.

Или, если вы хотите обработать его таким же effect образом после получения результата от identityServiceClient.create функции, вы можете попробовать что-то вроде следующего:

 createUser$ = createEffect(() =>
    this.actions$.pipe(
        ofType(fromUserManagement.createUserAction),
        mergeMap((action) => {
            return this.identityServiceClient.create(action.result).pipe(
                map((response) => {
                    this.messageService.open('User has been created!', 'success');
                    this.router.navigateByUrl('/app/users/list');
                }),
                switchMap((response) =>
                    // Call here all the extra requests (request1$, request2$) depending on the response,
                    // and the `forkJoin` will emit the value once all the request have been completed
                    forkJoin({ response1: request1$, response2: request2$ }).pipe(
                        // if you want to do extra work with the results that come from request1$ amp; request2$ you can do it here using `tap` operator
                        tap(({ response1, response2 }) => console.log(response1, response2)),
                        // and after that you need to return back the action you want to dispatch which is `createUserActionSuccess` in your case
                        map(() => fromUserManagement.createUserActionSuccess(response))
                    )
                ),
                catchError(() => {
                    this.messageService.open('Something went wrong!', 'danger');
                    return fromUserManagement.createUserActionError;
                })
            );
        })
    )
);
 

Ответ №2:

Для краткого ознакомления с тем, с чем вы работаете, похоже, что в ваших кодовых базах используется NgRx для управления состоянием. Действия, которые вы видите отправляемыми, задокументированы здесь. Аналогично, эффекты задокументированы здесь. С этим изображением можно суммировать весь жизненный цикл взаимодействия:

https://user-images.githubusercontent.com/3605268/61837742-1851a300-ae54-11e9-97ed-9fb862d0bbf8.png

Приведенный выше ответ, безусловно, является решением, это просто зависит от того, что вам нужно сделать с идентификатором пользователя после создания пользователя. Когда действия отправляются в NgRx, ожидается, что какой бы объект ни выполнял отправку, он запустил это действие и вообще не ожидает возвращаемого значения ( userId в вашем случае).

Похоже, что если пользователь уже существует, вы просто запрашиваете данные пользователя в API. Итак, если это так, то я бы создал эффект, который прослушивает успешное создание пользователя ( fromUserManagement.createUserActionSuccess ), а затем запускает то же самое действие, уже упомянутое для запроса ( fromSettings.getCurrentUserInfoAction ) :

 updateUserAfterCreation$ = createEffect(() =>
    this.actions$.pipe(
        ofType(fromUserManagement.createUserActionSuccess),
        switchMap((action) => 
          fromSettings.getCurrentUserInfoAction()
        )
    )
);
 

В приведенном выше примере я предполагаю, что диспетчеризация fromSettings.getCurrentUserInfoAction запускает эффект, который уже есть в вашей кодовой базе, и использует любые userId уже сохраненные в хранилище (поскольку getCurrentUserInfoAction не принимает никаких параметров, таких как userId ).

Этот шаблон можно повторить для любого конкретного случая использования — если вам поручено обновить профиль пользователя сразу после создания, то вместо указания switchMap на fromSettings.getCurrentUserInfoAction вы могли бы использовать fromSettings.updateNewUserAction . Лично я предпочитаю отделять эти цепочки эффектов вместо того, чтобы помещать их в один большой канал в предыдущем ответе — это проще тестировать, легче читать и легче устранять неполадки.