【问题标题】:Ngrx causes hanging issue until page is reloaded when subscribing to store.dispatchNgrx 导致挂起问题,直到订阅 store.dispatch 时重新加载页面
【发布时间】:2020-12-09 22:42:29
【问题描述】:

我对 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,
  };

【问题讨论】:

  • 能否也展示一下监听GetUserByEmail的效果以及更新selectUserByEmail所基于的状态的reducer?
  • @AndreiGătej 是的,请查看编辑
  • 如果将 catchError 用作“getUserByEmal.pipe(catchError()..)...”会发生什么?
  • 我认为最初的问题是由于错误,效果会被取消订阅。所以它认为在 switchMap 的内部 observable 中移动 catchError 可以防止这种情况发生
  • @AndreiGătej 对不起,我误解了你最初的意思,但解决了它!太感谢了。喜欢就加一个答案,否则我就加一个

标签: angular ngrx store


【解决方案1】:

最初的问题是由于错误,效果会被取消订阅。所以你可以做的是将catchError移动到switchMap的内部可观察对象中:

...

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

...

【讨论】:

    猜你喜欢
    • 2019-07-15
    • 1970-01-01
    • 2011-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-09
    • 2019-11-16
    • 1970-01-01
    相关资源
    最近更新 更多