Reputation: 2028
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
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
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
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