Angular RouteReuseStrategy

Tacettin Sertkaya
5 min readJul 30, 2023

--

What is our problem?

We use a big third-party library to handle complex data operations. When the library starts, it quickly gets the data we need. Although both the start-up process and data retrieval take some time, we solved this by using an external service for the start-up, so it only happens once. After that, we save the requests for faster performance. Problem solved? Well, almost.

Even though we made our application work as efficiently as possible, a sharp-eyed user might still notice a slight delay when navigating. Sometimes, this leads to a brief moment where the data table is empty or a loading spinner appears, even though the data is already cached.

This short delay happens while Angular recreates the component, uses our dependable ngOnInit method, and gets everything going. Interestingly, this mostly occurs when moving between routes that use different component classes.

Solution: RouteReuseStrategy

To make the behavior better and more controllable, we can use the powerful RouteReuseStrategy provided by the @angular/router package. By using this abstract class, we can decide exactly which route components should be kept for later use and which ones should be removed when we navigate to a different route.

export abstract class RouteReuseStrategy {
/** This method determines whether this route (and its nested routes) should be detached for potential reuse at a later time. */
abstract shouldDetach(route: ActivatedRouteSnapshot): boolean;
  /**
* This function stores the detached route for potential later reuse.
*
* If a null value is stored, it will effectively erase the previously stored value.
*/
abstract store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle|null): void;
/** This function determines whether this route (and its nested routes) should be reattached. */
abstract shouldAttach(route: ActivatedRouteSnapshot): boolean;
/** This function retrieves the previously stored route. */
abstract retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle|null;
/** This function determines whether a route should be reused. */
abstract shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean;
}

Here are three important methods, namely:

The shouldReuseRoute method lets us choose whether Angular should use the same component object again when moving between routes that use the same component class.

  • The shouldDetach and store methods allow us to decide when a component object should be saved and specify how and where it should be saved.
  • The shouldAttach and retrieve methods enable us to decide when a component object should be fetched and reconnected.

To better understand how to use it effectively, let’s also take a look at Angular’s default way of implementing this feature.

/**
* The base route reuse strategy provided by Angular
* only reuses routes when the matched router configs are identical.
* This ensures that components are not destroyed and recreated solely due to changes in the fragment or query parameters.
* In other words, the existing component is effectively reused in such scenarios.
 * The BaseRouteReuseStrategy can serve as a foundation for creating custom route reuse strategies. 
* You have the flexibility to design your own class that extends the BaseRouteReuseStrategy to implement a personalized route reuse behavior.
*/
export abstract class BaseRouteReuseStrategy implements RouteReuseStrategy {
/**
* This method determines whether the given route should be detached for potential reuse at a later time.
* For the `BaseRouteReuseStrategy`, this method always returns false
* */
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return false;
}
/**
* This method is a no-op; the route is never stored since the `BaseRouteReuseStrategy` does not detach routes for later re-use.
*/
store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void {}
/** The `shouldAttach` method returns false, indicating that the route (and its nested routes) should never be reattached. */
shouldAttach(route: ActivatedRouteSnapshot): boolean {
return false;
}
/** The retrieve method returns null since this strategy does not store routes for later re-use. */
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle|null {
return null;
}
/**
* This function determines whether a route should be reused.
* This strategy returns true when the future route configuration and the current route configuration are identical.
*/
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}
}

In our case, the method we are particularly interested in is shouldReuseRoute. We want to expand the behavior of navigating between components that use the same component class.

Let’s Create Custom RouteReuseStrategy

With a solid understanding of the RouteReuseStrategy’s functionality from the Angular code itself, we are now ready to create our custom strategy.

import {ActivatedRouteSnapshot, DetachedRouteHandle, BaseRouteReuseStrategy} from '@angular/router';
export class AppRouteReuseStrategy implements BaseRouteReuseStrategy {
public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return (future.routeConfig === curr.routeConfig) || future.data.reuseComponent;
}
}

The process is simple; we mainly extended it using future.data.reuseComponent, which allows us to easily decide whether we want to reuse each route or not in a declarative way.

Before we continue, we need to let Angular know that we want to change its default settings

import {AppRouteReuseStrategy} from './app-route-reuse-strategy';
import {RouteReuseStrategy} from '@angular/router';
@NgModule({
providers: [
{provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy}
]
})
export class AppModule {
}

After setting up our module as shown above, the only thing left to do is to tell Angular which routes we want to reuse by configuring them accordingly:

const routes: Routes = [
{
path: 'app',
component: SampleComponent,
data: {
reuseComponent: true
}
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}

And that’s it! Now, Angular will efficiently reuse our route whenever we move away from it and come back to it later. Quite impressive!

CONCLUSION

I hope your project is now running smoothly as expected!

The current approach seems to be working well for your requirements, but you may have had other specific ideas in mind when you came here. So, I encourage you to try out the other methods too, like manually storing and retrieving component objects. This way, you can learn more about how the route reuse strategy works and discover additional options for your project. Enjoy experimenting and have fun fine-tuning your application! If you need more help or have any questions, feel free to ask.

--

--