【问题标题】:How do I return an Observable to an outer promise in Effects of NgRX?如何在 NgRX 的影响中将 Observable 返回到外部承诺?
【发布时间】:2020-05-26 04:37:22
【问题描述】:

我有一个关于如何将 Observable 返回到外部承诺的问题。我的 effects.ts 中有以下内容:

  @Effect()
  login$: Observable<Action> = this.actions$.pipe(
    ofType(UserActions.LOGIN),
    switchMap((action: UserActions.LoginAction) => {

      this.db.auth.signInWithEmailAndPassword(action.payload.email, action.payload.password).then(data => {
        this.db.auth.currentUser.getIdToken().then(reply => {
          return this.http.post(environment.apiUrl+'/api/login', { token: reply }).subscribe(response => {
              if (response['valid'] === 'true') {
                localStorage.setItem('token', JSON.stringify(reply));
                this.router.navigate(['dash']);
              }
            });
        });
      }).catch(err => {
        console.log('signIn failed: ' + err.message);
      });
    })
  );

现在,我的总体目标是简单地登录 FireBase 以获取 ID 令牌并向后端提交发布请求。如果响应返回属性有效“true”,则继续导航到“dash”模块。

但是,我的问题是:

'(action: UserActions.LoginAction) => void' 类型的参数不能分配给'(value: LoginAction, index: number) => ObservableInput' 类型的参数。 类型 'void' 不可分配给类型 'ObservableInput'.ts(2345)

我相信这是因为我将我的回报声明放在了外部承诺中。但我不知道如何让它正确返回,因为 Firebase 使用 Promise,而 NgRX 使用 Observables!我正在尝试从承诺中返回一个 Observable...谢谢

【问题讨论】:

  • 首先有承诺,其次你没有返回一个动作

标签: angular firebase promise observable ngrx


【解决方案1】:

调用api后必须返回一个动作

  @Effect()
  login$: Observable<Action> = this.actions$.pipe(
    ofType(UserActions.LOGIN),
    switchMap((action: UserActions.LoginAction) =>
      from(this.db.auth.signInWithEmailAndPassword(action.payload.email, action.payload.password)),
    ),
    switchMapTo(from(this.db.auth.currentUser.getIdToken())),
    switchMap(token => this.http.post(environment.apiUrl + '/api/login', { token })),
    tap((response: any) => {
      if (response.valid === 'true') {
        localStorage.setItem('token', JSON.stringify(token));
        this.router.navigate(['dash']);
      }
    }),
    map(response => UserActions.LoginSuccess({ response })),
    catchError(error => UserAction.LoginError({ error })),
  );

【讨论】:

  • TypeError:无法读取 null 的属性“getIdToken”。在这种情况下,db.auth.currentUser 似乎为 null,并且必须以某种方式使用 db.authState 来首先确定用户?
  • @devteam6 登录步骤是什么?
  • 我想我应该使用 authState 来获取当前用户。然后从你从那里得到的用户,你可以调用 getIdToken(),它返回一个承诺。我从另一篇文章中读到它应该看起来像 this.afAuth.authState.pipe(take(1), switchMap((user) => { if (user) { return from(user.getIdToken()) }
  • 所以...首先 signInWithEmailAndPassword,然后 afAuth.authState.pipe 返回一个 user.getIdToken,从那里我们 http.post 令牌并等待 response.valid=="true"
  • 你解决了吗?如果没有,您可以在 stackblitz 上创建一个示例,以便我为您提供帮助
【解决方案2】:

您绝对是在正确的轨道上。我记得当我第一次学习 RxJS 时,我曾为这类事情苦苦挣扎。

我想指出几点:

  1. 效果应该返回动作。这就是错误消息所说的。如果您不想返回 Action,那么您必须这样做:@Effect({ dispatch: false })。但是,在这种情况下,您绝对应该在最后分派一个动作,例如:“用户成功登录”,并将用户令牌作为有效负载。然后,您可以使用 reducer 将其存储在您的状态中。但是,如果您真正想做的只是之后导航到/dash,那么请使用{ dispatch: false } 参数。
  2. 要将 Promise 转换为 Observable,请使用 rxjs 库中的 from 函数。因此,您可以将所有内容包装到您通常使用then 函数的位置:from(this.db.auth.signInWithEmailAndPassword(action.payload.email, action.payload.password))
  3. 在这种情况下避免使用switchMap。这将导致竞争条件。请改用exhaustMap。然后为每个 Promise 使用一个。所以它最终看起来像:

    @Effect() login$: Observable<Action> = this.actions$.pipe( ofType(UserActions.LOGIN), exhaustMap((action: UserActions.LoginAction) => from(this.db.auth.signInWithEmailAndPassword(action.payload.email, action.payload.password)), exhaustMap(data => from(this.db.auth.currentUser.getIdToken()), exhaustMap(reply => { return this.http.post(environment.apiUrl+'/api/login', { token: reply }), map(response => { if (response['valid'] === 'true') { localStorage.setItem('token', JSON.stringify(reply)); this.router.navigate(['dash']); } }); }); }) }) );

【讨论】:

  • 不要使用地图操作符产生副作用,使用点击操作符
【解决方案3】:
  @Effect()
  login$: Observable<Action> = this.actions$.pipe(
    ofType(UserActions.LOGIN),
    switchMap((action: UserActions.LoginAction) =>
      from(this.db.auth.signInWithEmailAndPassword(action.payload.email, action.payload.password)),
    ),
    // switchMapTo(from(this.db.auth.currentUser.getIdToken())),
    switchMapTo(from(this.db.authState.pipe(
      take(1),
      switchMap((user)=>{
        if (user)
          return from(user.getIdToken());
        else
          return of(null);
      })
    ))),
    switchMap(token => this.http.post(environment.apiUrl + '/api/login', { token: token })),
    tap((response: any) => {
      if (response.valid === 'true') {

        console.log("effects success");

        // localStorage.setItem('token', JSON.stringify(token));
        this.router.navigate(['dash']);
      }
    }),
    map(response => new UserActions.LoginSuccessAction({ response }))//,
    // catchError(error => new UserActions.LoginFailureAction({ error }))
  );

在做了一些认真的研究之后,我编写了正确的答案。感谢 Chris 的贡献。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-09
    • 2016-10-25
    • 1970-01-01
    • 2016-09-18
    • 2020-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多