Sunday 25 June 2023

DestroyRef Angular 16 - How DestroyRef Made Develpoer's Life Easier

We are all familiar with the importance of properly handling subscriptions when a component is being destroyed. Neglecting to do so can lead to memory leaks, causing our application, browser, or client machine to progressively slow down due to excessive memory usage and accumulated garbage.

When we subscribe to observables or other resources within a component, it's crucial to unsubscribe or complete those subscriptions when the component is no longer in use. If we fail to clean up these subscriptions during the destruction phase, they will continue to hold references to resources, preventing them from being garbage-collected. As a result, memory consumption grows over time, impacting the performance and responsiveness of our application.

Angular 16 has recently been released, offering a range of exciting new features. One noteworthy addition is the introduction of an injectable entity called DestroyRef. This entity can be injected into various Angular building blocks, including components and services.

DestroyRef untilDestroyed Angular 16

By utilizing DestroyRef, you can register a callback function within these building blocks. This callback function will be executed just before the associated scope is destroyed. It provides a convenient way to perform cleanup operations or additional logic before the scope is removed.

The DestroyRef feature simplifies the handling of pre-destruction actions across different Angular components and services. It ensures that necessary cleanup tasks are executed reliably, resulting in more robust and efficient Angular applications. With this new capability, developers can have better control over the destruction process of Angular scopes and enhance the overall performance of their applications.

All right! You might be wondering, "Isn't the ngOnDestroy hook already available in Angular? Why do we need DestroyRef?"

While ngOnDestroy is available, DestroyRef allows us to create reusable logic for performing cleanup tasks when a scope is destroyed, without the need for inheritance. This simplifies the implementation process and reduces complexity.

ngOnDestroy Exmaple:

typescript
import { Component, OnDestroy, OnInit } from '@angular/core'; import { of, Subscription } from 'rxjs'; @Component({ selector: 'app-thecoderevisited', templateUrl: './thecoderevisited.component.html', styleUrls: ['./thecoderevisited.component.css'] }) export class ThecoderevisitedComponent implements OnInit, OnDestroy { subscriptions: Subscription[] = []; ngOnInit(): void { this.subscriptions.push(of([]).subscribe()); } ngOnDestroy(): void { // Cleanup logic this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe()); } }

In the above code, we have an Angular component called ThecoderevisitedComponent that implements the OnInit and OnDestroy interfaces. Inside the component, there is an array called subscriptions of type Subscription[], which will store all the subscriptions we create.

In the ngOnInit lifecycle hook, we push a new subscription to the subscriptions array using the of([]).subscribe() method. This is just a placeholder observable (of([])) that doesn't emit any values. You can replace it with your actual observable.

In the ngOnDestroy lifecycle hook, we iterate over the subscriptions array and call unsubscribe() on each subscription. This ensures that all subscriptions are properly unsubscribed when the component is destroyed, preventing any memory leaks.

Limitation with ngDestory() :
It's unnecessary to emphasize that these activities had to be duplicated in every component where subscriptions are used. It appears as an additional task to perform and extra code to include and remember.

As developers, we continually strive to improve and simplify our lives. Some implementations I've come across introduce a base component solely to centralize subscription management in one place. Personally, I'm not particularly fond of this approach as it introduces an additional layer of abstraction and inheritance. This, in turn, adds complexity to unit testing and requires additional considerations for super constructor calls in each component.

As a developer, finding a balance between code simplicity and maintainability is crucial. While consolidating subscription logic in a base component can provide a centralized approach, it's important to weigh the trade-offs and consider alternative solutions that minimize complexity and improve testability. An alternative solution is DestroyRef.

DestroyRef :

Using DestroyRef is a straightforward process. From angular 16 onwards we can inject the DestroyRef provider as follows and register a destroy callback in the following manner:

typescript
@Component({ selector: 'foo', standalone: true, template: '', }) class ThecoderevisitedComponent { constructor() { inject(DestroyRef).onDestroy(() => { // Perform necessary cleanup tasks when the component is destroyed }); } }

As an example, we can create an untilDestroyed operator that relies on DestroyRef:

typescript
export function untilDestroyed() { const subject = new Subject(); inject(DestroyRef).onDestroy(() => { subject.next(true); subject.complete(); }); return <T>() => takeUntil<T>(subject.asObservable()); } @Directive({ selector: '[appFoo]', standalone: true, }) export class ThecoderevisitedDirective { private untilDestroyed = untilDestroyed(); ngOnInit() { interval(1000) .pipe(this.untilDestroyed()) .subscribe(console.log); } }

In this example, the untilDestroyed operator creates an observable that emits values until the associated scope is destroyed. It relies on the DestroyRef functionality to handle the destruction event. The ThecoderevisitedDirective uses this untilDestroyed operator within its ngOnInit lifecycle hook to subscribe to an interval observable and log the emitted values.

By leveraging DestroyRef and related utilities, we can simplify the process of performing cleanup tasks and ensure that our Angular components and directives are properly managed when destroyed.

Read more,

No comments:

Post a Comment

Seven front-end development trends in 2023-2024

With the increasing prevalence of apps in the digital landscape , the role of front-end developers remains essential. While apps aim to ove...

Popular Posts