NGRX 7 - 即使在ngrx / effects中调度不同类型的新动作,也会陷入无限循环

问题描述 投票:0回答:2

我正在尝试在@ 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']);
      // }
    })
  );

login.component.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();
    }
  }

enter image description here

编辑:新发现当我返回一个http调用(来自authService)observable时,如:

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

这个错误正在发生(即请求陷入无限循环)。但是,当我通过重新调整下面的新观察结果伪装api时,它就不是了。

   return new Observable<LoginResponse>((observer) => {
      if (email === '[email protected]' && password === 'abc123') {
        const data: LoginResponse = {
          accessToken: 'dadsfjhsjdahlfjh#324jk34h23343kkjlsadsads',
          refreshToken: 'jfjsdg-32432-sdf4543-sdff4234-3424-3434',
          user: {
            email: '[email protected]',
            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();
    });
angular rxjs rxjs6 ngrx-store ngrx-effects
2个回答
0
投票

尝试使用此代码

  // Listen for the 'LOGIN' action
  @Effect()
  login$: Observable <Action> = this
    .actions$
    .pipe(ofType<authAction.Login>(authAction.LoginActionTypes.LOGIN), mergeMap(action => this.authService.login(action.payload).pipe(
    // If successful, dispatch success action with result
    map(data => ({type: authAction.LoginActionTypes.LOGIN_SUCCESS})),
    // If request fails, dispatch failed action
    catchError(() => of({type: authAction.LoginActionTypes.LOGIN_FAIL})))));

  /* Pass { dispatch: false } to the decorator to prevent dispatching.
  Sometimes you don't want effects to dispatch an action, for example when you only want to log or navigate.
  But when an effect does not dispatch another action, the browser will crash because the effect is both 'subscribing' to and 'dispatching'
  the exact same action, causing an infinite loop. To prevent this, add { dispatch: false } to the decorator. */
  @Effect({dispatch: false})
  loginSuccess$ = this
    .actions$
    .pipe(ofType(authAction.LoginActionTypes.LOGIN_SUCCESS), tap(() => this.router.navigate(['/portal'])));

您不需要使用return this.authService.login或返回操作类型这种方法更好更干净,因为每次想要调度操作类型时都没有新的

我的意见可能是this.loginForm.resetForm();行触发你的动作多次发送所以我建议你换成这个

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();
  }

我的模拟登录服务

  login(userCredentials: any): Observable<any> {
    if (userCredentials.account !== 'test') {
      return throwError('Invalid username or password');
    }
    return of({name: 'User'}); //change your code here
  }

我正在使用return of()来让observable知道我已经收到了我需要的数据,所以效果不再需要在可观察流上订阅

如果您需要任何帮助,请告诉我


0
投票

经过大量的调试后我终于找到了错误:它在我的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);
        }
      })
    );
  }

在此,只要auth状态改变,就会调度新请求,从而导致无限循环。要解决这个问题,我必须使用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);
        }
      })
    );
  }
© www.soinside.com 2019 - 2024. All rights reserved.