Reputation: 830
I have implemented a NgRx Router-Store for my Angular project in the common way (https://ngrx.io/guide/router-store/configuration).
My "Problem" is that the serialize-method of my CustomRouterStateSerializer
seems to be called multiple times, when a routerLink gets triggered by clicking the corresponding html element of a component.
You'll find a minimal example application of the following description on StackBlitz.
My implementation
This is my router.reducer.ts
file which contains the RouterStateUrl-Interface and the Serializer-Class:
export interface RouterStateUrl {
url: string;
queryParams: Params;
params: Params;
random: number;
}
export class CustomRouterStateSerializer implements RouterStateSerializer<RouterStateUrl > {
serialize(routerState: RouterStateSnapshot): RouterStateUrl {
const { url, root: { queryParams } } = routerState;
// Random number to be able to match console output to router-state later (with NgRx Store DevTools)
const random = Math.random();
console.warn(`CustomRouterStateSerializer called by ${url}, random: ${random}`);
let state: ActivatedRouteSnapshot = routerState.root;
while(state.firstChild){
state = state.firstChild;
}
const { params } = state;
return {url, queryParams, params, random };
}
}
This is my app.module.ts
file:
/*[...]*/
imports: [
/*[...],*/
StoreModule.forRoot(reducers, {
metaReducers,
runtimeChecks: {
strictStateImmutability: true,
strictActionImmutability: true
}
}),
StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.development }),
EffectsModule.forRoot([AppEffects]),
StoreRouterConnectingModule.forRoot({
serializer: CustomRouterStateSerializer,
navigationActionTiming: NavigationActionTiming.PostActivation,
}),
]
/*[...]*/
Detailed description and output
Lets say my app currently displays an overview of some projects (url: /projects) and a routerLink gets triggert to switch the component to show an overview of jobs (url: /jobs). The console will print three messages:
CustomRouterStateSerializer called by /projects, random: 0.0896547559010431
CustomRouterStateSerializer called by /jobs, random: 0.7662025752972623
CustomRouterStateSerializer called by /jobs, random: 0.07919176016307328
NgRx Store DevTools are showing several Actions as expected:
@ngrx/router-store/request
router: {
state: {
url: '/projects',
queryParams: {},
params: {},
random: 0.31957045879116797
},
navigationId: 2
}
@ngrx/router-store/navigation
router: {
state: {
url: '/jobs',
queryParams: {},
params: {},
random: 0.7662025752972623
},
navigationId: 3
}
@ngrx/router-store/navigated
router: {
state: {
url: '/jobs',
queryParams: {},
params: {},
random: 0.7662025752972623
},
navigationId: 3
}
As you can see the state of @ngrx/router-store/navigation
and @ngrx/router-store/navigated
are identical. Furthermore their random number is identical to the second console output. The random number of @ngrx/router-store/request
belongs to the state of the old projects-view.
The output of NgRx Store DevTools seem to be as expected. But I don't understand when and what called the serialize method where the other console outputs were triggert. I can not find any random numbers of the first and the third console output in any state. Now I'm asking myself if I have made an error (implementing stuff) or if this is just normal behaviour (but why?). Maybe some of you can tell me.
Upvotes: 1
Views: 1685
Reputation: 211
I tried out a few things, but in the end it helped to take a look at the source code of @ngrx/router-store
.
What this module essentially does is to listen to all router events and dispatch actions. The important piece of code is this one: https://github.com/ngrx/platform/blob/master/modules/router-store/src/router_store_module.ts#L240-L275
But I don't understand when and what called the serialize method where the other console outputs were triggert.
You can see here that there are three situations where serialize()
can be called:
NavigationStart
(directly)NavigationEnd
(through the dispatchRouterNavigation()
and dispatchRouterNavigated()
methods)RoutesRecognized
(through dispatchRouterNavigated()
)Each of these events call serialize()
separately to avoid derived state: The router is the source of the router state and it can change at any given time.
Thus, when the serialized state is needed, it will not be stored somewhere but newly calculated each time instead.
This is why the function is being called multiple times. However, since the serializer function should be pure, this is not a problem at all but rather part of the design.
I can not find any random numbers of the first and the third console output in any state.
The reducer will only put the router state into the store on navigation
, cancel
and error
– but not on request
and navigated
.
That means, the only random number you will see in the store is the one originating from the navigation
action.
All others are just used "on the way".
Now I'm asking myself if I have made an error (implementing stuff) or if this is just normal behaviour (but why?).
Your implementation looks good and you can be relieved: This is normal and intended behavior! 🙂 Just be aware that your serializer function should be pure.
Hope this helps!
Upvotes: 3