I have an Angular 16 app, where I lazy load modules in app.routes file, as shown below:
export const APP_ROUTES: Routes = [
{
loadChildren: () => import('./modules/users/users.routes').then(m => m.USERS_ROUTES),
path: 'users',
},
{
loadChildren: () => import('./modules/settings/settings.routes').then(m => m.SETTINGS_ROUTES),
path: 'settings',
},
]
File users.routes with routing for users module looks like this:
export const USERS_ROUTES: Route[] = [
{
path: '',
providers: [UsersHttpRepository, UsersComponentStore],
resolve: [() => inject(UsersComponentStore).loadUsers$()],
children: [
{
path: '',
component: UsersListPage,
},
{
path: ':userId',
component: UserDetailsPage,
},
],
},
];
Architecture of my app is very modular, where certain parts of it (/users, /settings etc) are separated modules, without overlapping or importing some common parts. I decided to go with NGRX component-store, because the data I keep in individual modules relates only to them, it doesn't go beyond them, so there's no need for it to be available globally. BUT I wanted one instance of component-store working across all the components in specific module (I would call it module-store), so the data can be shared between all the components.
I am aware, that this is not the exact use case for component store, which should be provided directly into a component, instead of a route.
The component store itself is very basic, part of here is shown below:
export const initialState: UsersComponentState = {
users: []
userDetails: null
};
@Injectable()
export class UsersComponentStore extends ComponentStore<UsersComponentState> {
readonly users = this.selectSignal(state => state.users);
readonly userDetails = this.selectSignal(state => state.userDetails);
readonly loadUsers$ = this.effect<void>(_ =>
_.pipe(
switchMap(() =>
this.repository.loadUsers().pipe(
tapResponse({
next: users =>
this.patchState({users}),
error: (error: HttpErrorResponse) => errorLogger(error)
})
)
)
)
);
constructor(private readonly repository: UsersHttpRepository) {
super(initialState);
}
}
What concerns me here, is that when user leaves the route, and navigates somewhere else outside users module, the ngOnDestroy hook is not executed, so the instance of this component-store is never destroyed. After coming back to the /users route, all the data is still available there.
It is a different behavior from when component-store is provided directly into a component and destroy with it.
I understand that this is probably a correct behavior, because once route and module is lazy loaded it is never destroyed, so the providers that i put in routes array here:
export const USERS_ROUTES: Route[] = [
{
path: '',
providers: [UsersHttpRepository, UsersComponentStore],
are also never destroyed.
My question is whether I should be worried about this? Ultimately, I want to have a component-store like this one in each of my module and I am concern about some memory leaks or unfinished subscriptions.
Whether you have memory leaks or unfinished subscriptions or not is completely up to your code. No one can tell you that just by your question here.
But what you can try is to use an empty shell/container component that will register
UsersComponentStorein its providers, so all shell's children will get this instance. After shell component is destroyed, its providers will be destroyed as wellUPDATE: My apologies. Actually, you don't even need an empty shell component. My suggestion should work with an empty route without a component as well.