1

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 the 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, 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 fireAuthUser, 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?

Audwin Oyong
  • 2,247
  • 3
  • 15
  • 32

3 Answers3

0

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)

navnath
  • 3,548
  • 1
  • 11
  • 19
0

Use takeUntil operator. 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 destroying observable when the component is destroyed.

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);
                    // ...
                }
         });
    }
}
Audwin Oyong
  • 2,247
  • 3
  • 15
  • 32
Den
  • 650
  • 6
  • 15
0

you could just use ngOnDestroy to unsubscribe?

export class Foo implements OnInit {

    private sub: Subscription;
    constructor(public auth: AuthService) { }

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

    ngOnDestroy() {
      this.sub.unsubscribe()
    }

}
bryan60
  • 28,215
  • 4
  • 48
  • 65