RouteReuseStrategy provider allows to control the behaviour of the Angular route and components lifecycle.

Everytime we navigate between components Angular destroys the previous component and creates the new one. In many situations we might not want this, because every time we load a component we might run again expensive operations like http requests.

The documentation about this service is not great, so I decided to write down some documentation followed by an example.

How does it work?

RouteReuseStrategy interface defines 5 methods:

shouldReuseRoute This method is called everytime we navigate between routes. The future is the route we are leaving (not sure why is called future) and curr is the route we are landing. If it returns TRUE the routing will not happen (which means that routing has not changed). If it returns FALSE then the routing happens and the rest of the methods are called.

shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {

// default action

return future.routeConfig === curr.routeConfig;

}

shouldAttach: This method is called for the route just opened when we land on the component of this route. Once component is loaded this method is called. If this method returns TRUE then retrieve method will be called, otherwise the component will be created from scratch:

shouldAttach(route: ActivatedRouteSnapshot): boolean;

retrieve: This method is called if shouldAttach returns TRUE, provides as parameter the current route (we just land), and returns a stored RouteHandle. If returns null has no effects. We can use this method to get any stored RouteHandle manually. This is not managed by the framework automatically. It is our responsibility to implement it:

retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null;

shouldDetach: It is invoked when we leave the current route. If returns TRUE then the store method will be invoked:

shouldDetach(route: ActivatedRouteSnapshot): boolean;

store: This method is invoked only if the shouldDetach returns true. We can manage here how to store the RouteHandle. What we store here will be used in the retrieve method. It provides the route we are leaving and the RouteHandle:

store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void;

Example of use

Let see a typical example:

Search Form Component loads /search-form route. We make a search Navigate to /search-results route that loads Search Results Component Click a result and navigate to /detail/1 that loads Detail Component Click Browser back button to return to navigate to the /search-results route that loads again the Search Result Component

The step 4 will cause the Search Result Component to run again the same search, making a useless GET request to the API.

We want to cache and avoid total reloading of the Search Result Component (and preserve the scrolling) on the step 4 and only reload it when navigating from step 1 to step 2.

cache-route-reuse.strategy.ts:

import { RouteReuseStrategy } from '@angular/router/';

import { ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';

export class CacheRouteReuseStrategy implements RouteReuseStrategy {

storedRouteHandles = new Map<string, DetachedRouteHandle>(); allowRetriveCache = {

'search-results': true

}; shouldReuseRoute(before: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { if (this.getPath(before) === 'detail' && this.getPath(curr) === 'search-result') {

this.allowRetriveCache['search-results'] = true;

} else {

this.allowRetriveCache['search-results'] = false;

} return before.routeConfig === curr.routeConfig;

} retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {

return this.storedRouteHandles.get(this.getPath(route)) as DetachedRouteHandle;

} shouldAttach(route: ActivatedRouteSnapshot): boolean {

const path = this.getPath(route);

if (this.allowRetriveCache[path]) {

return this.storedRouteHandles.has(this.getPath(route));

}



return false;

} shouldDetach(route: ActivatedRouteSnapshot): boolean {

const path = this.getPath(route);

if (this.allowRetriveCache.hasOwnProperty(path)) {

return true;

} return false;

} store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void {

this.storedRouteHandles.set(this.getPath(route), detachedTree);

} private getPath(route: ActivatedRouteSnapshot): string {

if (route.routeConfig !== null && route.routeConfig.path !== null) {

return route.routeConfig.path;

} return '';

}

}

app.module.ts insert the provider of the reuse strategy in the providers configuration:

...

providers: [{

provide: RouteReuseStrategy,

useClass: CacheRouteReuseStrategy

}],

...

I am using the shouldReuseRoute method only to understand the navigation flow: is the user navigating from Detail Component back To Search Results Component? If yes then load the last cached component of Search Results (shouldAttach => true, retrieve is invoked), if no then re-create the component and perform a fresh search.