The Problem

Every RxJS subscription in an Angular component continues running after the component is destroyed. If the component is created and destroyed multiple times (navigation, dialog, tabs), subscriptions accumulate, causing memory leaks and stale data updates.

Symptoms

  • Memory usage grows over time
  • Old component data appears in new component instances
  • Multiple API responses process for a single user action
  • Angular DevTools shows destroyed components in memory
  • Console logs from "dead" components

Real Error Scenario

```typescript @Component({...}) export class DashboardComponent { ngOnInit() { // This subscription NEVER gets cleaned up this.dataService.getData().subscribe(data => { this.data = data; // Runs even after component destroyed! });

// This interval runs forever interval(5000).subscribe(() => { this.refreshData(); }); } } ```

When the user navigates away, both subscriptions continue running. If they navigate back, NEW subscriptions are created. After 10 navigations, 10 intervals fire every 5 seconds.

How to Fix It

Fix 1: takeUntilDestroyed (Angular 16+)

```typescript import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

@Component({...}) export class DashboardComponent { private destroy$ = inject(DestroyRef);

ngOnInit() { this.dataService.getData() .pipe(takeUntilDestroyed()) .subscribe(data => { this.data = data; });

interval(5000) .pipe(takeUntilDestroyed()) .subscribe(() => this.refreshData()); } } ```

Fix 2: Async Pipe (Cleanest)

typescript
@Component({...})
export class DashboardComponent {
  data$ = this.dataService.getData();
  timer$ = interval(5000).pipe(
    startWith(0),
    switchMap(() => this.dataService.getData())
  );
}
html
<div *ngIf="data$ | async as data">
  {{ data | json }}
</div>

The async pipe automatically subscribes and unsubscribes.

Fix 3: takeUntil with Subject

```typescript @Component({...}) export class DashboardComponent implements OnDestroy { private destroy$ = new Subject<void>();

ngOnInit() { this.dataService.getData() .pipe(takeUntil(this.destroy$)) .subscribe(data => this.data = data); }

ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } } ```

Fix 4: Subscription.add for Multiple Subscriptions

```typescript @Component({...}) export class DashboardComponent implements OnDestroy { private subscriptions = new Subscription();

ngOnInit() { this.subscriptions.add( this.dataService.getData().subscribe(data => this.data = data) ); this.subscriptions.add( interval(5000).subscribe(() => this.refreshData()) ); }

ngOnDestroy() { this.subscriptions.unsubscribe(); // Cleans up all at once } } ```