首页 > 解决方案 > NGRX 7 - 即使在 ngrx/effects 中调度不同类型的新动作时也陷入无限循环

问题描述

我正在尝试在@angular/core 7.1.0 和@ngrx/store 7.0 中实现登录。现在的问题是,当我从我的登录组件调度一个新的登录动作时,它在登录效果中正确侦听,但即使在调度新的 LoginSuccess 动作时,登录动作也会陷入无限循环,直到 LoginFailure 动作发生。(当我停止后端时服务)。

auth.effects.ts

  @Effect()
  login$: Observable<Action> = this.actions$.pipe(
    ofType(AuthActionTypes.LOGIN),
    map((action: Login) => action.payload),
    switchMap(payload => {
      console.log('SwitchMap: Login [Effect]: payload', payload);
      return this.authService.login(payload.email, payload.password).pipe(
        map((loginResponse: LoginResponse) => {
          console.log('loginResponse:', loginResponse);
          return new LoginSuccess(loginResponse);
        }),
        catchError(error => {
          console.log(error);
          return of(new LoginFailure({ error: error }));
        })
      );
    })
  );


  @Effect({ dispatch: false })
  loginSuccess: Observable<any> = this.actions$.pipe(
    ofType(AuthActionTypes.LOGIN_SUCCESS),
    map((action: LoginSuccess) => action.payload),
    tap((loginResponse: LoginResponse) => {
      console.log('Login_Success [Effect]: payload', loginResponse);
      localStorage.setItem('accessToken', loginResponse.accessToken);
      localStorage.setItem('refreshToken', loginResponse.refreshToken);
      localStorage.setItem('user', JSON.stringify(loginResponse.user));
      // if (loginResponse.user.isSuperAdmin) {
      //   this.router.navigate(['/admin/dashboard']);
      // } else {
      //   this.router.navigate(['/dashboard']);
      // }
    })
  );

登录组件.ts

onSubmit() {
    // Will triggered only when form is submitted
    if (this.loginForm.valid) {
      console.log('Form Submitted: values', this.loginPayload);
      this.store.dispatch(new Login({ email: this.loginPayload.username, password: this.loginPayload.password }));
      this.loginForm.resetForm();
    }
  }

在此处输入图像描述

编辑:新发现 当我返回一个 http 调用(来自 authService)时,可以观察到:

return this.http.put<LoginResponse>('/api/v1/entrance/login', body);

这个错误正在发生(即请求陷入无限循环)。但是,当我通过像下面这样重新调整新的 observable 来伪造 api 时,事实并非如此。

   return new Observable<LoginResponse>((observer) => {
      if (email === 'superadmin@xyz.com' && password === 'abc123') {
        const data: LoginResponse = {
          accessToken: 'dadsfjhsjdahlfjh#324jk34h23343kkjlsadsads',
          refreshToken: 'jfjsdg-32432-sdf4543-sdff4234-3424-3434',
          user: {
            email: 'superadmin@xyz.com',
            name: 'Superadmin',
            isSuperAdmin: true,
            id: 1,
            isLdapUser: false,
            isAdUser: false,
            lastSeenAt: new Date().getTime()
          }
        };
        observer.next(data);
      } else {
        observer.error({ error: 'invalid credentials.' });
      }
      observer.complete();
    });

标签: angularrxjsrxjs6ngrx-storengrx-effects

解决方案


经过大量调试,我终于找到了错误:,它在我的 authorizeRequest 拦截器中。以前我的这个拦截器的代码是:

   intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.store.select('auth').pipe(
      switchMap((authState: fromAuth.State) => {
        if (authState.user && authState.accessToken) {
          const secureReq = req.clone({
            headers: req.headers.set('Authorization', 'Bearer ' + authState.accessToken)
          });
          return next.handle(secureReq);
        } else {
          return next.handle(req);
        }
      })
    );
  }

在这种情况下,每当身份验证状态发生变化时,都会分派新请求,从而导致无限循环。为了解决这个问题,我必须使用take(1) 运算符

所以我的代码现在变成了:

   intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.store.select('auth').pipe(
      take(1),
      switchMap((authState: fromAuth.State) => {
        if (authState.user && authState.accessToken) {
          const secureReq = req.clone({
            headers: req.headers.set('Authorization', 'Bearer ' + authState.accessToken)
          });
          return next.handle(secureReq);
        } else {
          return next.handle(req);
        }
      })
    );
  }

推荐阅读