Update
From https://stackoverflow.com/a/43673630/828547
[...]with LazyLoaded modules, route.routeConfig.path is empty when executing shouldAttach
Using explicitly defined additional developer-defined data rather than route.routeConfig.path
works.
Original Question
When implementing RouteReuseStrategy
to provide a custom route reuse strategy, I've observed.
OnInit
and theconstructor
are called when entering a component that has previously been stored for reuseOnDestroy
is NOT called when leaving a component that has previously been stored for reuse
Intuitively, it makes sense that OnDestroy
is not called for a component that should be reused later on.
However, it seems counter-intuitive that the constructor
and OnInit
are called for components that are being reused. It's common to place initialization code in these functions, and so the component appears new each time.
There's no shortage of tutorials on RouteReuseStrategy
(1, 2, 3, 4) , but I can't find anything confirming whether my observations are correct.
In the implementation of the RouteReuseStrategy
interface below, MyComponent
is stored for reuse. According, its OnDestroy
hook is NOT called when navigating away from the component. However, upon reentering the component, its constructor and OnInit
hook are called.
import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
export class CustomRouteReuseStrategy implements RouteReuseStrategy {
private storedRoutes = new Map<string, DetachedRouteHandle>();
/**
* Determines if this route (and its subtree) should be detached to be reused later.
* If shouldDetach returns true, then we need to store the route as we want to reuse later.
* Called when leaving a route.
*/
shouldDetach(route: ActivatedRouteSnapshot): boolean {
if (!route.routeConfig?.path) {
return false;
}
// Prevents the MyComponent component from being destroyed (OnDestroy not called).
return ['my-component'].indexOf(route?.routeConfig?.path) > -1;
}
/**
* Stores the detached route. Called when leaving a route.
*/
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
if (!route.routeConfig?.path) {
return;
}
const path = this.getPath(route);
this.storedRoutes.set(path, handle);
}
/**
* Determines if this route (and its subtree) should be reattached.
* If shouldAttach returns true, we want to reuse a previously stored route instead of building new components.
* Called when entering a route.
*/
shouldAttach(route: ActivatedRouteSnapshot): boolean {
if (!route.routeConfig?.path) {
return false;
}
const path = this.getPath(route);
return this.storedRoutes.has(path);
}
/**
* Retrieves the previously stored route.
*/
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
if (!route.routeConfig?.path) {
return null;
}
const path = this.getPath(route);
return this.storedRoutes.get(path) || null;
}
/**
* Determines if this route (and its subtree) should be detached to be reused later.
* Called everytime we navigate between routes.
*/
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
// Reuse the route if the new route has the same config as the previous one.
return future.routeConfig === curr.routeConfig;
}
private getPath(route: ActivatedRouteSnapshot): string {
if (route.routeConfig !== null && route.routeConfig.path !== null) {
return route.routeConfig?.path || "";
}
return "";
}
}
Questions
- Should
constructor
andOnInit
be called for components that are reattached (reused)? - If answer to 1. is "yes" - where should initialization code be placed such that it's run only when the component is NOT being reused?
Stackblitz https://stackblitz.com/edit/angular-ivy-9fs3tg?file=src/app
To mirror my application the stackblitz has the following module hierarchy
- app
- home
- manager
- detail
Each module has routing
/
loadsAppComponent
/home
loadsHomeComponent
/home/manager
loadsManagerComponent
/home/detail
loadsDetailComponent
The custom routing strategy reuses the ManagerComponent
so that upon returning to it, the list of details is not re-rendered.
To recreate the issue:
- Go to /home/manager
- Open the Console provided by Stackblitz to see the logging
- Click on
/home/detail/1
- Look in the console and see that
-----> stored route with path "manager"
is logged - Click "back to manager" and see that
ManagerComponent.constructor
andManagerComponent.ngOnInit
are logged.
The logging shows that the route was attached
shouldAttach(manager)
-----> Yes!
retrieve(manager)
-----> retrieved route with path "manager"
If the route was attached, why are the constructor and OnInit
of the manager component called?