Angular RxJs订户是isn';t在存储更改后立即更新

Angular RxJs订户是isn';t在存储更改后立即更新,angular,rxjs,ngrx-store,Angular,Rxjs,Ngrx Store,项目: 我正在Angular中使用NgRx和RxJs构建一个登录组件 当前设置 在login.component.ts文件的构造函数中,我订阅了“login”状态 单击UI中的“登录”按钮后,我将在以下流程中发送一个操作: GetUser Action>GetUser Effect HTTP request>Success=GetUserSuccess Action>GetUserSuccess Reducer将有效负载(jwt令牌)添加到存储中,并将“isLoggedIn”属性更改为true

项目:

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

当前设置

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

单击UI中的“登录”按钮后,我将在以下流程中发送一个操作:

GetUser Action>GetUser Effect HTTP request>Success=GetUserSuccess Action>GetUserSuccess Reducer将有效负载(jwt令牌)添加到存储中,并将“isLoggedIn”属性更改为true

如果失败,GetUserFailure操作将从效果中调度,GetUserFailure Reducer除了向“errorMsg”属性下的存储添加错误消息之外,不会添加任何内容

所需的下一步

在login.component.ts中,由于我已经订阅了登录状态,我希望检查'isLoggedIn'是真是假。如果为true,
this.router.navigateByUrl('/dashboard')否则,从存储中获取“errorMsg”,并将其作为字符串在页面上输出

问题

我在一个从登录按钮触发的函数中调度一个操作,但我也希望在检查完商店后导航。如果我单击按钮一次,我可以得到一个控制台日志,其中说明“isLoggedIn”为false(就像我单击按钮时一样)。但是,如果我再次单击该按钮,它将变为true

如何确保login.component.ts文件正在主动监视存储,并在该值更改为true后路由到“dashboard”?我假设下面的代码可以做到这一点,但显然不行

代码

内部构造:

this.store.subscribe(state => (this.loginState$ = state));
在单击Login按钮时触发的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$); 
  }
总之


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

Jamie!谢谢你提出这么详细的问题!对任务有一个完整的描述总是好的

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

由于NgRx代码的异步性质,您无法立即看到变量中的更改。简短描述-当函数未完成时,事件仍在队列中。如果将其包装在
setTimeout(()=>console.log(this.loginState$)中,

如何解决您的案件?

我对这个问题的建议是创建一个带有登录状态的Observable并订阅它。之后,检查是否有用户登录或发生错误。 在本例中,您的代码如下所示:

类日志组件{
isLoggedIn$:可观察;
构造函数(私有存储:存储){
this.isLoggedIn$=this.store.select(state=>state.isLoggedIn);
}
onSubmit(){
const loginCredentials=this.loginForm;
this.store.dispatch(new GetUser(loginCredentials.value));
//首先是防止多次调用。它可能会触发两到三次。在您的代码中,解决方案可能会有所不同
this.isLoggedIn$.pipe(first()).subscribe(isUserLoggedId=>{
if(isUserLoggedIn){
这个。重定向到仪表板();
}否则{
这是一条消息();
}
}
}
建议

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

p.S.以更简单的方式使用此异步可观察数据


我希望我能帮助你!致以最良好的祝愿。

感谢那些回答/帮助你的人

修复程序

// 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或函数中的存储

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

据我所知,因为我订阅了构造函数之外的状态,所以它会根据需要立即更新状态


另外,通过将订阅分配给常量变量,我可以在Ngondestry lifecycle hook中取消订阅,以防止内存泄漏。

您应该创建一个选择器,用于选择isLoggedIn属性。查看文档: