1
votes

I have a component that subscribes to multiple slices of state in the constructor.

 @ViewChild(PComponent) PGrid: PComponent;

ngOnInit() {
    this.store.pipe(
          select(fromReports.getProcessState),
          takeWhile(() => this.componentActive))
          .subscribe(selectedProcess=> {
            if (selectedProcess) {
              this.Data= selectedProcess.Data;
  }
          });

  this.store.pipe(
          select(fromReports.getProcessAnalysis),
          takeWhile(() => this.componentActive))
          .subscribe(analysisData => {
            if (analysisData) {
              this.PGrid.LoadData(analysisData);
              }
          });
}

The first subscription subscribes to an array of objects while the second subscription calls a method in another component (@ViewChild(PComponent) PGrid). The LoadData() method in PGrid component depends upon this.Data from the first subscription.

When I run the code I get undefined for this. Data although there selectedProcess.Data has value

this.store.pipe(
          select(fromReports.getProcessState),
          takeWhile(() => this.componentActive))
          .subscribe(selectedProcess=> {
            if (selectedProcess) {
              this.Data= selectedProcess.Data; //undefined

  }
          });

This means when the second slice of the state is subscribed and LoadData() method is called it errors out as this.Data is undefined.

I believe this is due to how Observable work and since they are async one function may not return the value before the other.

Que: 1 Why this.Data= selectedProcess.Data; sets this.Data to undefined even though selectedProcess.Data has value

Que 2: I have looked at flatMap and switchMap but since I am new to RxJS and NgRx I am confused about how to leverage them. Any tips?

P.S On a similar note, I am subscribing to 2 slices separately in my ngOnInit(), is this a preferred way or should I combine them?

1

1 Answers

1
votes

Everything is a stream.

Every observable stream should be named with a $.

Avoid using subscribe until the end.

combineLatest will not emit an initial value until each observable emits at least one value.

tap can be used for side effects and logging and does not affect the stream. It's the "I don't know what I'm doing operator". I use it liberally.

Pseudo code

import { of, combineLatest } from "rxjs";
import { map, delay, tap, switchMap } from "rxjs/operators";

const DATA = "selectedProcessData";
const PROCESS_ANALYSIS = "analysis";

const service = {
  getData: () => {
    return of(DATA).pipe(delay(2000));
  },
  loadData: data => {
    return of(data).pipe(delay(2000));
  }
};
const store = {
  getProcessAnalysis: () => {
    return of(PROCESS_ANALYSIS).pipe(delay(2000));
  }
};

let getSelectedProcessData = null;
const getSelectedProcessData$ = service.getData().pipe(
  tap(data => console.log(`getSelectedProcessData::${data}`)),
  tap(data => (getSelectedProcessData = data))
); // mimick getting data from a service

const getAnalysisData$ = store.getProcessAnalysis(); // mimick selection of state

const loadAnalysisData$ = combineLatest(
  getSelectedProcessData$,
  getAnalysisData$
).pipe(
  tap(([getSelectedProcessData, getAnalysisData]) =>
    console.log(`combineLatest::${getSelectedProcessData}, ${getAnalysisData}`)
  ),
  map(([getSelectedProcessData, getAnalysisData]) => getAnalysisData),
  switchMap(getAnalysisData => service.loadData(getAnalysisData))
);

loadAnalysisData$.subscribe(loadedAnalysisData =>
  console.log(`loadedAnalysisData::${loadedAnalysisData}`)
);

Stackblitz