Ngrx 导致挂起问题,直到订阅 store.dispatch 时重新加载页面

Ngrx causes hanging issue until page is reloaded when subscribing to store.dispatch

我对 Ngrx 商店很陌生,但我有一个似乎有效的实现。

我注意到我正在构建的登录屏幕中有一个奇怪的行为。我想以特定方式处理 GetUserFailures,所以我订阅了 ScannedActionsSubject 并按操作类型进行过滤。

export class LoginComponent implements OnInit {

  constructor(
    private userService: UserService,
    private store: Store<AppState>,
    private actions$: ScannedActionsSubject,
) {
    ...
  }

  ngOnInit() {
    this.actions$
      .pipe(filter((action: any) => action.type === '[User] Get User Failure'))
      .subscribe((action: any) => {
        const error = action.payload.error.response;
        this.handleError(error);
        if (error && error.includes('not found')) {
          this.snackBar.open('Sorry, we have no record of that email address.', 'OK');
        } else if (error && error.includes('Invalid credentials')) {
          this.snackBar.open('The password you entered is incorrect.', 'OK');
        }
        if (this.loading) this.loading = false;
      });
  }

  async login(event: any) {
    if (event) event.preventDefault();

    // if the user is logging in for the first time, show update password form
    if (this.validateForm(this.loginForm)) {
      this.loading = true;
      const email = this.loginForm.controls.email.value;
      email && this.store.dispatch(new GetUserByEmail(email));
      this.store.select(selectUserByEmail)
        .pipe(filter(result => result.user !== (null || undefined)))
        .subscribe(
          async (result) => {
            this.user = result.user;
            if (this.user && this.user.status === 'Pending') {
              const email = this.loginForm.controls.email.value;
              const password = this.loginForm.controls.password.value;
              email && password && this.userService.validatePasswordHash(email, password).subscribe(
                (result) => {
                  if (result) {
                    this.loading = false;
                    this.loginStepper.selectedIndex = 2;
                    this.hidePassword = true;
                  }
                }, 
                (error) => {
                  this.handleError(error);
                  if (error.error && error.error.response.includes('Invalid credentials')) {
                    this.snackBar.open('The password you entered is incorrect.', 'OK');
                    this.loading = false;
                    return;
                  }
                },
              );
            } else if (this.user && this.user.status === 'Active') {
              localStorage.setItem('USER_AUTHD', 'true');
              await this.router.navigateByUrl('/home');
              this.snackBar.open(`Welcome back, ${this.user.recipientFullName.split(' ')[0]}!`, 'OK', { duration: 4000 });
            } else {
              console.log('neither active nor pending!', this.user);
            }
            this.loading = false;
            return;
          },
          (error) => {
            console.log('Error getting user by email', error);
            return;
          },
          () => {  // complete
            if (this.loading) this.loading = false;
          },
        );
  }
}

发生错误时,错误得到了正确处理,但是当我尝试再次启动我的登录过程时,store.select 部分无限期挂起,我似乎无法弄清楚原因。就可读性而言,它绝对不是最好的实现,但这是我为工作功能想出的全部。

重申一下,在第一次登录尝试失败后,每次登录尝试都会在登录流程中“挂起”,特别是 this.store.select(selectUserByEmail),但很难追踪挂起的原因,因为我只能看到至于 GetUserByEmail 的网络请求,它有效。

我希望能够“重新加载”系统以进行更多登录尝试。

GetUserByEmail 效果:

@Injectable()
export class UserEffects {

  @Effect()
  getUserByEmail$: Observable<any> = this.actions$.pipe(
    ofType<GetUserByEmail>(UserActionsEnum.GetUserByEmail),
    switchMap((action) => this.userService.getUserByEmail(action.payload)),
    map((response) => new GetUserSuccess(response)),
    catchError((error) => of(new GetUserFailure(error))),
  );
}

减速器:

case UserActionsEnum.GetUserByEmail:
  return {
    ...state,
    user: action.payload?.data,
    loading: false,
  };

最初的问题是由于错误,效果将被取消订阅。所以你可以做的是在 switchMap 的内部 observable 中移动 catchError

...

switchMap(
 (action) => this.userService.getUserByEmail(action.payload).pipe(catchError(...))
),

...