Reputation: 5915
After tracking down unexpected requests being made I've found that my app's global user
state's slices emit upon every navigation.
What are potential causes of an NGXS state slice
Here is the chronological order of events in the app
Obviously we don't want slices to emit for no changes in data.
#4 demonstrates a side-effect where an unnecessary request is triggered.
Here is the code
@Selector()
public static scope(state: DataModel) {
console.log('scope triggered');
return state.scope;
}
// Actions only trigger once as expected
@Action(GetScopes)
getAvailableScopes(ctx: StateContext<DataModel>) {
return this.apiService.get('/scopes').pipe(
tap((result) => {
ctx.setState(
patch<DataModel>({
scope: result.scopes,
})
);
})
);
}
The app is not complex but I cannot replicate unexpected slices emitting in StackBlitz Github issue
It should be noted that the state does not change!
ngxsOnChanges(change: NgxsSimpleChange) { ...
only fires when the app inits and is never again triggered within in this flow despite the state's selectors emitting on navigation.
Upvotes: 2
Views: 472
Reputation: 5915
The answer to the issue above is that state management has several weaknesses which your architecture will have to monitor.
One of them being that new subscriptions to selectors cause the selector to emit.
Navigating to a new route in the app can load another feature module. That feature module subscribes to the same Selectors causing the old subscriptions to emit despite the fact that they are about to be unsubscribed.
We solved this by setting up a resolver to ensure that all global states have been fetched and are available to every component. Then these globally shared states don't have the timing issues and can be used without subscribing to them.
resolver
@Injectable({ providedIn: 'root' })
export class AppShellResolver implements Resolve<unknown> {
constructor(private store: Store) {}
resolve(route: ActivatedRouteSnapshot): Observable<any> {
return this.store
.dispatch([
new GetAvailableScopes(),
... <-- other required states for UI
])
...
}
}
usage
this.scopes = this.store.selectSnapshot(UserState.scopes);
Upvotes: 3