RxJs 订阅者在存储更改后不会立即更新



项目:

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

当前设置

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

点击UI中的"登录"按钮,我在以下流程中调度一个操作:

GetUser Action>GetUser Effect HTTP请求>Success=GetUserSuccess Action>GetUserSuc成功还原器将有效负载(jwt令牌(添加到存储中,并将"isLoggedIn"属性更改为true。

如果失败,则从效果中调度GetUserFailure Action,GetUserFailureReducer将在"errorMsg"属性下向存储区添加除错误消息之外的任何内容。

所需的下一步

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

问题

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

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

代码

内部构造函数:

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

总结

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

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

为什么您无法立即看到更改

由于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,以反应式编程方式思考始终是一个不错的选择。应用程序中的任何数据都是Observable,并且每个操作都是异步的。它可以在未来为你节省几个小时的生命。

p。S.要以更简单的方式处理此异步Observable数据,可以使用选择器。

我希望我帮了你!顺致敬意,

感谢那些提供帮助的人!

修复

// 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生命周期挂钩中取消订阅,以防止内存泄漏。

最新更新