NGRX 7 - застрял в бесконечном цикле даже при отправке нового действия другого типа в ngrx / эффектах - PullRequest
0 голосов
/ 28 апреля 2019

Я пытаюсь реализовать вход в систему в @ angular / core 7.1.0 и @ ngrx / store 7.0.Теперь проблема в том, что когда я отправляю новое действие Login из моего компонента входа в систему, он корректно прослушивает эффект Login, но даже при отправке нового действия 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), наблюдаемый как:

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

эта ошибкапроисходит (т.е. запрос застревает в бесконечном цикле).Но когда я подделываю 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();
    });

Ответы [ 2 ]

0 голосов
/ 29 апреля 2019

После долгих отладок я наконец нашел ошибку :, Это было в моем 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);
        }
      })
    );
  }
0 голосов
/ 28 апреля 2019

Попробуйте с этим кодом

  // 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 или тип действия return, этот подход лучше и чище, так как вам не нужно обновлять каждый раз, когда вы хотите отправить тип действия

И, по-моему, это 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 (), чтобы дать понять наблюдаемой, что я получил данные, которые мне нужны, поэтому больше не нужно подписываться на наблюдаемый поток

Пожалуйста, дайте мне знать, если вам нужна помощь

...