Home > Mobile >  behaviorSubject return only when there is something new
behaviorSubject return only when there is something new

Time:06-01

I have the following problem, when I call the getStudentsList function, it goes to the server to find the new values that I need, but the behaviorSubject returns the current value again (This causes me problems because it load the view with the previous values) while I wait for the server information to return. How can I do to avoid this behavior?

  getStudentsList(params): Observable<StudentsListGrid> {
    if (this.studentListSubject$ === undefined) {
      this.studentListSubject$ = new BehaviorSubject<StudentListGrid>(null);
    }
    this.refetchStudentList(params);
    return this.studentListSubject$.asObservable().pipe(filter((value) => value !== null));
  }

  refetchStudentList(gridParams) {
    const subscription = this.http.post<StudentListGrid>(this.baseUrl   'GetStudents', {gridParams} ,httpOptions).pipe(map(data => this.setDefaultValuesToStudent(data))).subscribe(
      (response) => {

        this.studentListSubject$.next(response);
        subscription.unsubscribe();
      });
  }

CodePudding user response:

Based on the definition of BehaviorSubject from doc

BehaviorSubject: A variant of Subject that requires an initial value and emits its current value whenever it is subscribed to

Solution 1

For a quick solution, you can set null before you return Observable in the getStudentsList function:

  getStudentsList(params): Observable<StudentsListGrid> {
    if (this.studentListSubject$ === undefined) {
      this.studentListSubject$ = new BehaviorSubject<StudentListGrid>(null);
    }
    this.refetchStudentList(params);
    // add this line
    this.studentListSubject$.next(null);
    return this.studentListSubject$.asObservable().pipe(filter((value) => value !== null));
  }

The reason is you empty the last value of BehaviorSubject by passing null so the return of this function always returns a fresh value.

Solution 2

Another way (better way) is to use distinctUntilChanged from RxJs library, I suppose your name become different in each response:

 getStudentsList(params): Observable<StudentsListGrid> {
    if (this.studentListSubject$ === undefined) {
      this.studentListSubject$ = new BehaviorSubject<StudentListGrid>(null);
    }
    this.refetchStudentList(params);
    return this.studentListSubject$.asObservable().pipe(
             filter((value) => value !== null),
             distinctUntilChanged((prev, curr) => prev.name === curr.name));
  }

I hope this answer helps you.

CodePudding user response:

As a rule, you do not want to subscribe to something for the purposes of updating another observable or subject. Instead, chain events to outcomes.

private studentListTrigger$ = new Subject();

readonly studentList$ = concat(
  of(null), // triggers a refresh the first time something subscribes to studentList$
  this.studentListTrigger$
).pipe(
  switchMap( // if a new request, drop any previous request in progress.
    () => this.http.post<StudentListGrid>(
      this.baseUrl   'GetStudents',
      {gridParams},
      httpOptions
    ).pipe(
      catchError(() => EMPTY) // don't let the error of any given attempt break studentList$
    )
  ),
  filter((value) => value !== null),
  shareReplay(1) // No matter how many subscribers, as long as there's at least one subscriber, do all of the above exactly once and then emit the last value to any late subscribers just like a BehaviorSubject does
);

refreshStudentList(): void {
  this.studentListTrigger$.next();
}

Now, whatever consumes studentList$ will receive the last-emitted result, plus a new student list every time it's refreshed.

  • Related