Home > Software engineering >  Angular12, rxjs: safe to use takeWhile without onDestroy?
Angular12, rxjs: safe to use takeWhile without onDestroy?

Time:09-27

I have a general confusion with takeWhile.

What I want to achieve:

  • Wait for fireAuthUser$ Observable to have a value
  • once that happens, do some stuff
  • if fireAuthUser$ Observable does not receive a value, cancel once component is left
export class AuthService implements OnDestroy {
    fireAuthUser: BehaviorSubject<firebase.User | undefined> = new BehaviorSubject<firebase.User | undefined>(undefined);
    public readonly fireAuthUser$: Observable<firebase.User | undefined> = this.fireAuthUser.asObservable();
    private subscription?: Subscription;

    constructor(public fire: AngularFireAuth) {
        this.subscription = this.fire.authState.subscribe((fireAuthUser) => {
            if (fireAuthUser) {
                this.fireAuthUser.next(fireAuthUser);
                // ...
            }
        });
    }

    ngOnDestroy(): void {
        this.subscription?.unsubscribe();
    }

    doSomeStuff(): void {
       //do some stuff
    }

}
export class Foo implements OnInit {

    constructor(public auth: AuthService) { }

    ngOnInit(): void {
         this.auth.fireAuthUser$.pipe(takeWhile((fireAuthUser) => fireAuthUser === undefined)).subscribe({
          complete: () => this.auth.doSomeStuff()
        });
    }

}

The code above works. However, according to Angular/RxJs When should I unsubscribe from `Subscription` by using takeWhile, the observable execution will not be cancelled on ngDestroy of the component. So from my understanding, I need to manually set a .next value in my ngOndestroy, as otherwise my takeWhile would never cancel? Use case would be, i.e., if the user cannot login (no fireAuthUser exists)

However, I guess I cannot simply put a ngOnDestroy(): void { this.auth.fireAuthUser.next(undefined); } as this would always erase my fireAuth user, which is a problem if the user does have a fireAuthUser object? So what should I do in this case? Or did I misunderstand this remark and my code is safe to use?

CodePudding user response:

Use TakeUntil. I love to create a BaseComponent that extend the class that have observable.

@Component({
    template: ''
})
export abstract class BaseComponent implements OnDestroy {
    protected unsubscribe: Subject<void> = new Subject();

    ngOnDestroy(): void {
        this.destroy();
    }

    private destroy(): void {
        this.unsubscribe.next();
        this.unsubscribe.complete();
    }
}

after this class you can extend all the class you want, and use takeUntil method for destory observable when component is destory

export class LeadEditComponent extends BaseComponent implements OnInit {
    ngOnInit(): void {
         this.subscription = this.fire.authState
            .pipe(
                 takeUntil(this.unsubscribe)
             )
            .subscribe((fireAuthUser) => {
                if (fireAuthUser) {
                    this.fireAuthUser.next(fireAuthUser);
                    // ...
                }
         });
    }
}

CodePudding user response:

Use takeUntil Operator. It will listen to this.auth.fireAuthUser$ untill current component is destroyed

import { Subject, takeUntil } from 'rxjs';

export class Foo implements OnInit{
  private destroy$ = new Subject();
  constructor() {}

  ngOnInit(): void {
    this.auth.fireAuthUser$
      .pipe(takeUntil(this.destroy$))
      .subscribe({
        next: (data) => console.log(data),
        complete: () => this.uponComplete(),
      });
  }

  uponComplete() {
    console.log('Finally completed');
  }

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

See demo here (click on Go to Auth timer will start and click on Home it will unsubscribe)

  • Related