Jadi untuk menggambar, ini kode saya saat ini:

ngOnInit(): void {
    this._pbsaAccountSelectorService.selectedAccount$.subscribe((pSelectedAccount) => {
        this.getSelectedAccountData(this._pbsaAccountSelectorService.selectedProfile.ProfileId, pSelectedAccount.AccountNumberForRequests);
    });
}
getSelectedAccountData(ProfileId: string, AccountNumberForRequests: string) {
    if(!this._rewardsService.currentSpend) {
      this._rewardsService.getCurrentSpend(ProfileId, AccountNumberForRequests).subscribe((res: any) => {
        this._rewardsService.currentSpend = res
      })
    }

    combineLatest([
      this._rewardsService.getEnigmaTransactions(AccountNumberForRequests),
      this.refresher$,
    ]).pipe(
      map(([val]) => val),
      takeUntil(this._pbsaAccountSelectorService.selectedAccount$),
    ).subscribe(res => {
      this.transactionList = res;
      this.filteredTransactionList = res;
    })
  }

Perilaku yang dimaksudkan adalah bahwa setiap kali SelectAccount$ Subject memancarkan nilai, itu akan mengaktifkan panggilan getCurrentSpend dan getEnigmaTransactions API. Tetapi selain itu, saya juga perlu mengaktifkan panggilan getEnigmaTransactions secara manual di bawah skenario tertentu, seperti jika beberapa kriteria filter berubah.

Untuk mencoba mencapai ini, saya menempatkan panggilan getEnigmaTransactions di combineLatest dengan subjek penyegaran.

Masalahnya adalah jika akun berubah, saya harus membatalkan combineLatest yang ada, jika tidak, itu hanya akan membuat lebih banyak dan lebih banyak langganan. Jadi saya mencoba untuk mengambil takeUntil yang akan membatalkan setiap kali akun berubah, tetapi saya pikir apa yang terjadi adalah segera setelah akun yang dipilih pertama dipancarkan, semuanya dibatalkan dan tidak pernah menyala lagi.

Memang, meskipun implementasi ini berhasil, saya mungkin akan mencari solusi yang lebih elegan, karena ini sepertinya cara yang buruk dalam melakukan sesuatu.

Bagaimana saya bisa mencapai perilaku yang saya inginkan? Mungkin menggunakan SwitchMap?

0
Craig Armstrong 12 Mei 2021, 13:21

2 jawaban

Jawaban Terbaik
  1. Langganan bersarang akan mengarah ke aliran data bersarang yang pada gilirannya akan menyebabkan kebocoran memori saat komponen dihancurkan.
  2. Gunakan switchMap untuk memetakan dari satu yang dapat diamati ke yang lain.
  3. Saya akan menggunakan combineLatest di sini berlebihan. Jika getEnigmaTransactions() perlu dipicu dalam dua contoh terpisah, pisahkan keduanya. Memiliki dua contoh terpisah di mana itu bisa dipicu.

Coba yang berikut ini

  • iif untuk mengembalikan yang dapat diamati secara kondisional
  • forkJoin untuk memicu beberapa yang dapat diamati secara paralel
  • takeUntil dengan Subject untuk menutup langganan saat komponen ditutup
closed$ = new Subject<any>();
enigmaTransactions$ = (acctNum: any) => this._rewardsService.getEnigmaTransactions(acctNum);

ngOnInit() {
  this._pbsaAccountSelectorService.selectedAccount$.pipe(
    switchMap((pSelectedAccount: any) =>
      iif(
        () => (!this._rewardsService.currentSpend),
        forkJoin({
          currentSpend: this._rewardsService.getCurrentSpend(
            this._pbsaAccountSelectorService.selectedProfile.ProfileId,
            pSelectedAccount.AccountNumberForRequests
          ),
          enigmaTransactions: this.enigmaTransactions$(
            pSelectedAccount.AccountNumberForRequests
          )
        }),
        this.getEnigmaTransactions$(
          pSelectedAccount.AccountNumberForRequests
        )
      )
    ),
    takeUntil(this.closed$)      // <-- close in `onDestroy` hook
  ).subscribe({
    next: (res: any) => {
      if (!!res.currentSpend) {  // <-- `forkJoin` was triggered
        // do something
        // res.currentSpend
        // res.enigmaTransactions
      } else {                   // <-- `forkJoin` was not triggered
        // do something
      }
    },
    error: (error: any) => {
      // handle error
    }
  });
}

onFilterChange(acctNum: any) {
  this.enigmaTransactions$(acctNum).pipe(
    takeUntil(this.closed$)      // <-- close in `onDestroy` hook
  ).subscribe(
    // handle response and error
  );
}

ngOnDestroy() {
  this.closed$.next();
}
0
Michael D 12 Mei 2021, 10:55

Anda seharusnya tidak pernah berlangganan dalam berlangganan di RXJS, Anda harus menggabungkan aliran masuk menjadi satu yang akan mendapatkan data Anda

Anda harus meletakkan segala sesuatu yang seharusnya memicu pengambilan data ke dalam observable, untuk membuatnya reaktif.

  • combileLatest membuatnya sedemikian rupa sehingga pada perubahan data input apa pun,
  • switchMap akan membatalkan permintaan sebelumnya jika filter berubah lebih cepat sehingga API dapat mengembalikan data.
class MyComponent {
  readonly selectedAccount$ = new Subject<any>();
  readonly filters$ = new Subject<any>();
  readonly data$ = combineLatest([
    this.selectedAccount$,
    this.filters$
  ]).pipe(
    switchMap(([account, filters]) => {
      return fetchDataObservable(account, filters);
    }),
    //shareReplay({ refCount: true, bufferSize: 1 }), // If this observable would be used at multiple places this will prevent fetching data twice
    //takeUntil(this.destroy), // If you would use this in other places than template only
  );
}
<pre>{{data$ | async | json}}</pre>
0
Akxe 12 Mei 2021, 10:39