Prevent multiple api calls with rxjs shareReplay

Issue

I need to prevent multiple api calls to certain routes.
I keep hearing that I should use shareReplay() to prevent multiple api calls especially when you are going between pages and coming back to the same page. I’ve used shareReplay() but when I look at the network tab it keeps on making the network request. Any ideas? I’ve made a stackblitz link here = https://stackblitz.com/edit/angular-ivy-jadosn

Here is my http.services code:

getWaterPokemon(): Observable<Filter[]> {
        return this.http.get<Filter[]>(`${this.getApi()}/type/5`, { responseType: 'json' }).pipe(
            map((clients: any) => clients.pokemon.map((client: any) => client.pokemon)),
            shareReplay()
          );
    }

And here is my water.component code:

ngOnInit(): void {
    this.waterPokemon$ = this.httpService.getWaterPokemon().pipe(
      shareReplay()
    );
  }

Everytime you enter the Water Pokemon page you’ll see the Network request happening.

Solution

This is not how the observables work.

Let me explain (it will be long but useful).

An observable is either of two types : HOT, or COLD.

A HOT obversable as an observable that emits a value continuously without getting closed. Think of it like an event listener on an HTML element.

A COLD observable is an observable that gets completed after its first call. Think of it like a Promise.

So, using http.get is COLD, whereas using shareReplay on an observable makes it HOT

What you are doing here is the following :

  • Go to the water page
  • Ask the service to fetch data from the API
  • Go to another page
  • Repeat

With your current code, all the service is doing is returning a HOT observable everytime you call the fetching function (which is useless in this case).

What you want to do is to STORE the result of the fetching, so that you don’t have to call the service everytime.

The idea would be

// Service
class FetchService() {
  
  data = new BehaviorSubject<any[]>([]);
  data$ = this.data.asObservable();

  constructor(private http: HttpClient) {
    http.get('url').subscribe(res => this.data.next(res));
  }
}

// Component

class MyComponent {

  data$ = this.service.data$;

  constructor(private service: FetchService) {}
}

This way, the service refreshes the data only once, when it is instanciated. BUT, since the service is a singleton (unless made otherwise), its instance lives up even when you change pages (which is not the case for a component, that gets destroyed and re-created whenever you route in/out of it).

Then, the component only has to get the memoized data in your service, preventing your app from making further calls to the API.

Answered By – temp_user

Answer Checked By – Senaida (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.