Observable subscribe callback getting called mutiple times in Angular.
I had scenario where the subcribe callback was getting called multiple times. Before getting into the issue, let me give you a bit of a context.
So, I had parent component and a couple of child components. Same API data was being used across the child components. Hence, I made the API call in the parent component and create a Subject which communicated the data to the subcribed child components. Whenever I refreshed the parent and child component, the subcribe callback inside child components got triggered twice.
Here is the service file which made the API call and was setting the behaviour subject.
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class CommonService {
public sub = new BehaviorSubject<any>([]);
constructor(private http : HttpClient) { }
fetchData(){
return this.http.get('https://jsonplaceholder.typicode.com/users');
}
setData(data:[]){
this.sub.next(data);
}
}
And here is the child component which was subscribing to the Behavior subject.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { CommonService } from 'src/app/common.service';
@Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.css']
})
export class AdminComponent implements OnInit, OnDestroy {
private dataObs:any;
constructor(private common: CommonService) { }
ngOnInit(): void {
this.dataObs = this.common.sub.subscribe((res) => {
console.log('response in emitter ', res);
})
}
ngOnDestroy(): void {
if(this.dataObs){
this.dataObs.unsubscribe();
}
}
}
As I already said that the parent component made the API call and set the data using setData
method to communicate it to the subscribed components. Here is the parent component,
import { Component, OnInit } from '@angular/core';
import { CommonService } from '../common.service';
@Component({
selector: 'app-app-container',
templateUrl: './app-container.component.html',
styleUrls: ['./app-container.component.css']
})
export class AppContainerComponent implements OnInit {
constructor(private common : CommonService) { }
ngOnInit(): void {
this.common.fetchData().subscribe((response:any) => {
this.common.setData(response);
})
}
}
Now as seen in the above code, we are calling the subject once the API call has been made. Then why would the subscribe in the child component trigger twice ?
The Subject we are using here is BehaviorSubject
and it returns a value on Subscription even though it hasn’t received a next()
. So, whenever I refreshed the parent child component it subscribes to the service behavior subject and it receives the last value. And once the API call is done in parent component, it triggers the next()
and child receives the next subscribe callback.
So the issue here is our choice of subject. Since we only want the subcribe to trigger on receiving a next we should use a regular Subject
, use of which resolved the issue.