商店更改后,RxJs订户不会立即更新

时间:2019-11-18 11:01:58

标签: angular rxjs ngrx-store

项目:

我正在使用NgRx和RxJs在Angular中构建一个登录组件。

当前设置

在我的login.component.ts文件的构造函数中,我订阅了“登录”状态。

在用户界面中单击“登录”按钮后,我将按照以下流程发送操作:

GetUser操作> GetUser效果HTTP请求>成功= GetUserSuccess操作> GetUserSuccess Reducer将有效负载(jwt令牌)添加到存储中,并将'isLoggedIn'属性更改为true。

如果失败,则从效果中分派GetUserFailure Action,GetUserFailure Reducer不会将错误消息添加到“ errorMsg”属性下的商店中。

所需的下一步

在login.component.ts中,因为我已订阅登录状态,所以我想检查一下'isLoggedIn'是true还是false。如果为true,则this.router.navigateByUrl('/dashboard');,否则,从商店中获取“ errorMsg”,并将其作为字符串吐到页面上。

问题

我正在从登录按钮触发的函数中调度操作,但是我还想在检查商店后导航。如果单击一次按钮,我将获得一个控制台日志,指出“ isLoggedIn”为false(就像单击按钮时一样)。但是,如果我再次单击该按钮,它将变为true。

如何确保login.component.ts文件正在主动监视商店,并且在该值更改为true时路由到“仪表板”?我以为下面的代码可以做到,但显然不能。

代码

内部构造函数:

this.store.subscribe(state => (this.loginState$ = state));

在单击“登录”按钮时触发的loginHandler函数内部:

  loginHandler() {
    const loginCredentials = this.loginForm;

    this.store.dispatch(new GetUser(loginCredentials.value));

    // this.loginState$.isLoggedIn should be true, but it's false 
    // (presumably because it's checking before the HTTP request and reducer has fired)
    console.warn(this.loginState$); 
  }

摘要

我如何等待看到商店属性更改,然后基于此路由而不使用诸如设置间隔之类的东西?

2 个答案:

答案 0 :(得分:1)

杰米!谢谢您这么详细的问题!完整地描述任务总是很好的。

为什么您不能立即看到更改?

由于NgRx代码的异步特性,您无法立即看到变量中的更改。简短说明-事件尚未完成,事件仍在队列中。如果将其包装在setTimeout(() => console.log(this.loginState$))中,它将发生变化。

如何解决您的情况?

我对这个问题的建议是创建一个具有登录状态的Observable并订阅它。之后,检查用户是否已登录或发生了一些错误。 在这种情况下,您的代码将如下所示:

class LogginComponent {
 isLoggedIn$: Observable<boolean>;

 constructor(private store: Store<{isLoggedIn: boolean}>) {
    this.isLoggedIn$ = this.store.select(state => state.isLoggedIn);
 }

 onSubmit() {

   const loginCredentials = this.loginForm;

   this.store.dispatch(new GetUser(loginCredentials.value));

   // First is here to prevent multiple calls. It could trigger two or three times. In your code solution could be different
   this.isLoggedIn$.pipe(first()).subscribe(isUserLoggedId => {
     if(isUserLoggedIn) {
       this.redirectToDashboard();
     } else {
       this.showErrorMessage();
     }
   }
}

建议

使用NGRX,以反应式编程方式思考始终是一个不错的选择。应用程序中的任何数据都是可观察的,每个动作都是异步的。将来可以节省数小时的生命。

PS 。要以更简单的方式处理此异步可观察数据,可以使用selectors

希望我能对您有所帮助!最好的问候。

答案 1 :(得分:0)

感谢那些回答/帮助过的人!

修复

// Subscribe to the store, check if logged in, if so route, else get error message
this.userStateSubscription = this.store.subscribe(state => {
  this.loginState$ = state;
  if (this.loginState$.login.isLoggedIn) {
    this.router.navigateByUrl('/dashboard');
  } else {
    this.errorMsg = this.loginState$.login.error;
  }

简而言之

尽管我是在构造函数中订阅商店的,而不是在OnInit或函数中订阅的。

我只是将订阅移到了登录函数中,将函数本身扩展为在本地使用“ loginState $”(就像我之前所做的那样),但是随后根据该状态下的值进行了路由。

据我了解,由于我已订阅构造函数的OUTSIDE状态,因此它会根据需要立即更新状态。

此外,通过将订阅分配给const变量,我随后可以在NgOnDestroy生命周期挂钩中取消订阅,以防止内存泄漏。