How to Use DestroyRef in Angular v16

Create a reusable cleanup logic by using the DestroyRef provider

Angular logo by Angular PressKit / CC BY 4.0

Angular v16 brought a lot of new features, such as Angular signals, required component inputs, takeUntilDestroyed, DestroyRef, and more.

In this short article, we will focus on DestroyRef and — spoiler alert — how to use it to create a reusable function to unsubscribe from observables.

Let’s get started!

What is DestroyRef?

DestroyRef is a class that we can inject with dependency injection. We can then pass a callback to its onDestroy method to execute some cleanup logic right before the owning scope is destroyed.

@Component({...
})
export class SomeComponent {
  constructor(destroyRef: DestroyRef) {
    destroyRef.onDestroy(() => {
      // Put cleanup logic here
    })
  }
}

ngOnDestroy vs. DestroyRef

Simply put, the DestroyRef class and the ngOnDestroy lifecycle hook solve the same problem, but in a different way.

They both allow us to execute some cleanup logic before the scope is destroyed, where the scope can be an Angular component, directive, pipe, or service.

The difference is that DestroyRef is injectable (and thus more flexible), whereas ngOnDestroy is a method that we need to implement as part of the OnDestroy lifecycle hook interface.

By the way, the ngOnDestroy lifecycle hook method would run before DestroyRef.onDestroy(), if we used both of them in the same scope.

DestroyRef in Action

So, what can we do with DestroyRef?

Because DestroyRef is injectable, we can create a reusable function and put our cleanup logic in there.

For example, we can unsubscribe from observables to avoid memory leaks. Of course, we have takeUntilDestroyed (in developer preview), but let’s see an alternative solution.

export function destroyScope() {
  const subscriptions = new Subscription();
  inject(DestroyRef).onDestroy(() => {
    subscriptions.unsubscribe();
  })
  return subscriptions;
}

Now we can reuse this wherever we need it. We just call the function, get a Subscription object, and add our subscriptions to it.

// import destroyScope function
@Component({...
})
export class SomeComponent implements OnInit{
  private subscriptions = destroyScope();
  ngOnInit() {
    this.subscriptions.add(
      interval(1000).subscribe(console.log)
    );
  }
}

Did unsubscribing just become a piece of cake or what? 🤯

But… there is a tiny limitation you should know. Because the function uses the inject function, it can only be used in a constructor context. So, we can either call it as shown in the previous snippet or inside the construct.

We can’t call it, say, in ngOnInit or some other method. If we do, we will get the following runtime error.

Below you can find a StackBlitz demo to play around with.

Conclusion

DestroyRef does the same thing as ngOnDestroy, except it’s more flexible and elegant.

We showed how to use it by creating a reusable function to unsubscribe from observables.

Thank you for reading!