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