Derek Wu
Derek Wu

Reputation: 53

Why is behaviorsubject causing my function to run multiple times?

I have service in Angular where I am trying to create a addCart function that adds the argument menuItem to the behaviorSubject serviceCart that contains an array of values. Then, getCart is called which console logs whatever is in serviceCart.

Here is the code:

private serviceCart = new BehaviorSubject([]);

  initializeCart() {
    this.serviceCart.next([]);
  }

  getCart() {
    this.serviceCart.subscribe(result => console.log('SERVICECART:', result));
  }

  addCart(menuItem) {
    this.serviceCart.subscribe(result => {
      result.push(menuItem);
      this.serviceCart.next(result);
      this.getCart();
    });
  }

I want the addCart function to only console log one instance of the array. However, whenever I call addCart, it returns a thousands of instances of the serviceCart array and the error:

ERROR RangeError: Maximum call stack size exceeded
    at SafeSubscriber.__tryOrUnsub (Subscriber.js:191)
    at SafeSubscriber.next (Subscriber.js:122)
    at Subscriber._next (Subscriber.js:72)
    at Subscriber.next (Subscriber.js:49)
    at BehaviorSubject.next (Subject.js:39)
    at BehaviorSubject.next (BehaviorSubject.js:30)
    at SafeSubscriber._next (cart.service.ts:28)
    at SafeSubscriber.__tryOrUnsub (Subscriber.js:183)
    at SafeSubscriber.next (Subscriber.js:122)
    at Subscriber._next (Subscriber.js:72)

I am pretty new to RxJs so any help would be appreciated!

Upvotes: 0

Views: 1972

Answers (2)

Adrian Brand
Adrian Brand

Reputation: 21638

You should not be subscribing to observables in your service, it should be the consumers of your service that subscribe, ie the components. You should not be mutating objects stored in the behavior subject with push, you should create a new array with the spread operator. You should use the standard naming convention of ending observables with a $.

serviceCart$ = new BehaviorSubject([]);

addCart(menuItem) {
  this.serviceCart$.next([...this.serviceCart$.value, menuItem]);
}

and in your component you should add the observable as a property to the component

serviceCart$ = this.cartService.serviceCart$;

and in your template you subscribe with the async pipe

<ng-container *ngIf="serviceCart$ | async as serviceCart">
   // use the view variable serviceCart here which will automagically update
</ng-container>

Upvotes: 2

Zze
Zze

Reputation: 18805

You have an infinite loop

this.serviceCart.subscribe(result => { // do on serviceCart change
      result.push(menuItem);
      this.serviceCart.next(result); // change serviceCart
      this.getCart();
});

You might want to unsubscribe:

var sub = this.serviceCart.subscribe(result => { // do on serviceCart change
      result.push(menuItem);
      sub.unsubscribe();
      this.serviceCart.next(result); // change serviceCart
      this.getCart();
});

Upvotes: 1

Related Questions