Story:
Hi everyone. I am a newbies in use NgRx library. In my angular project I use router guard to prevent unauthenticated user to unauthenticated website.
My ideal is call an rest api to verify local storage token content. When rest api result is "success", before return of(true), do "store.dispatch(LoginSuccess)" to update login auth information in Store.
Problem:
When do dispatch action. The "LoginSuccess" action always execute infinite loop even setting { dispatch: false } in @Effect(). After try and search by google solution for it. This problem always in my project. Hope some friends on stackoverflow can give me some help.~ thank you.
part of codes:
auth.guard.ts
.....
....
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
const authData: {email: string, token: string} = JSON.parse(
String(localStorage.getItem('User')));
const authToken = authData && authData.token;
// loginServ is a REST API for verify token
return this.loginServ.VerifyToken(authToken).pipe(
concatMap((res: AuthResult) => {
if (res.result === 'success') {
this.store.dispatch(LoginSuccess({
isAuthenticated: true,
email: authData && authData.email,
token: authToken,
result: 'success',
message: 'success'
})); // <-- execute action infinite.
return of(true);
} else {
this.store.dispatch(LoginFailed({
isAuthenticated: false,
email: authData && authData.email,
token: authToken,
result: 'failed',
message: 'token illegal'
}));
localStorage.removeItem('User');
this.router.navigate(['']);
return of(false);
}
})
);
...
...
action.ts:
...
export const LoginSuccess = createAction(
'[auth] Login Success',
props<{
isAuthenticated: boolean,
email: string,
token: string,
result: string,
message: string,
}>()
);
....
...
reducer.ts
....
....
export const loginReducer = createReducer(
defaultState,
on(Login),
on(LoginSuccess, (state, userInfo) => {
console.log(state); // <-- for debug.
console.log(userInfo); // <-- for debug.
return {
...state,
...userInfo
};
}),
on(LoginFailed, (state, result) => {
return {
...state,
...result
};
}),
....
...
effect.ts
....
...
loginSuccess = createEffect(() => this.actions$
.pipe(
ofType(LoginSuccess),
map((user: any) => {
console.log(user);
const beStoreInfo = {
email: user.email,
token: user.token,
};
localStorage.setItem(
'User',
JSON.stringify(beStoreInfo));
return {
type: '[auth] Login Success',
isAuthenticated: true,
email: user.email,
token: user.token,
result: user.result,
message: user.message
};
}),
), {dispatch: false});
....
...
service_overview-routing.module.ts
....
// using lazy loading module
const routes: Routes = [
{
path: '',
canActivate: [AuthGuard],
component: ServiceOverviewComponent},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ServiceOverviewRoutingModule { }
....
app-routing.module.ts
Using lazy load module.
....
...
const routes: Routes = [
{ path: 'login', loadChildren: () => import('./login/login.module')
.then(m => m.LoginModule)},
{ path: 'register', loadChildren: () => import('./register/register.module')
.then(m => m.RegisterModule)},
{ path: 'service_overview', loadChildren: () => import('./service-overview/service-overview.module')
.then(m => m.ServiceOverviewModule)},
{ path: '', redirectTo: 'login', pathMatch: 'full' }
];
....
Environment:
Angular Cli: 8.0.3
NgRx Store/Effect: 8.0.1
OS: Ubuntu 18.04
..Thanks