Yehia A.Salam
Yehia A.Salam

Reputation: 2028

Do i need to unsubscribe from an Ngrx Select

I have a component as follows, where I have a button calling select_property when clicked. The thing is im not sure whether i need to unsubscribe in any way before reassigning $livevisitors on each click , not sure whether $livevisitors | async in the component template does this work for me.

export class LiveComponent{

    livevisitors$: Observable<LiveVisitor[]>;
    selected_property_id: number = 0;

    constructor(
            private store: Store<AppState>
        ) {

        this.livevisitors$ = this.store.select(selectAllLiveVisitors);

    }

    select_property(id){
        this.selected_property_id = id;

        if (id == 0){
            this.livevisitors$ = this.store.select(selectAllLiveVisitors);
        } else {
            this.livevisitors$ = this.store.select(selectLiveVisitorsByPropertyId, {property_id: id});
        }
    }

Upvotes: 4

Views: 2767

Answers (3)

mdabrowski
mdabrowski

Reputation: 76

When we subscribe in OnInit method there might be potential memory leaks. Example:

export class RegisterComponent extends BaseComponent implements OnInit {
    loading = false;

    constructor(private store: Store) {}

    ngOnInit(): void {
        this.store.select(fromAuth.selectAuthLoading)
           .subscribe(loading => this.loading = loading);
    }

}

When we use async pipes we are safe. Example:

HTML:

@if (recipe$ | async; as recipe) {
    <div> {{recipe.name}} </div>
}

TS:

export class RecipeDetailComponent implements OnInit {
    recipe$: Observable<Recipe | null> = this.store.select(fromRecipes.selectRecipeDetails);

    constructor(private store: Store) {
    }

    ngOnInit(): void {
    }
}

To also extend Or Shalmayev comment, we could actually extend components in which we are subscribeing.

TS base.component:

@Component({
   selector: 'app-base',
   standalone: true,
   template: ``
})
export class BaseComponent implements OnDestroy {
    destroyed$: Subject<void> = new Subject<void>();

    ngOnDestroy() {
        this.destroyed$.next();
        this.destroyed$.complete();
    }
}

TS register component:

@Component({
selector: 'app-register',
standalone: true,
imports: [],
templateUrl: './register.component.html',
styleUrl: './register.component.scss'
})
export class RegisterComponent extends BaseComponent implements OnInit {
    loading = false;

    constructor(private store: Store) {
        super();
    }

    ngOnInit(): void {
        this.store.select(fromAuth.selectAuthLoading)
            .pipe(takeUntil(this.destroyed$))
            .subscribe(loading => this.loading = loading);
    }
}

To wrap this up: I would prefer to use async pipe, but there is also possibility to just extend component and make it clean.

Upvotes: 0

Or Shalmayev
Or Shalmayev

Reputation: 315

However if you subscribe to the observable in your component, you introduce a potential memory leak:

export class LiveComponent{

    livevisitors$: Observable<LiveVisitor[]>;
    selected_property_id: number = 0;

    constructor(
            private store: Store<AppState>
        ) {

    }

     ngOnInit() {
        //Potential memory leak
        this.store.select(selectAllLiveVisitors).subscribe(() = > {...})

     }

}

if that is the case then you need to unsubscribe when the component is destroyed. An elegant solution to this is to declare a Subject property:

export class LiveComponent implements OnInit, OnDestroy {
    destroyed$: Subject<void> = new Subject<void>();
    livevisitors$: Observable<LiveVisitor[]>;
    selected_property_id: number = 0;

    constructor(
            private store: Store<AppState>
        ) {

    }

     ngOnInit() {
        // will unsubscribe when component is destroyed
        this.store.select(selectAllLiveVisitors)
           .pipe(takeUntil(this.destroyed$))
           .subscribe(() = > {...})

     }

     ngOnDestroy() {
        this.destroyed$.next();
        this.destroyed$.complete();
     }
}

Upvotes: 3

G&#233;r&#244;me Grignon
G&#233;r&#244;me Grignon

Reputation: 4238

The async pipe subscribe and unsubscribe for you. You don't need to manage manual unsubscription.

From the official documentation :

When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks.

Upvotes: 6

Related Questions