javascriptangularrxjssession-storage2-way-object-databinding

RxJS with Angular Two-way binding enter multiple times in catchError


I have a service that store items in the session storage with an expiry date. When i retrieve an item, if the expiry date is over, the service return null value :

The method to save items :

private saveSessionData<T>(key: string, value: T) {
    const now = new Date();
    const item = {
      value: value,
      expiry: now.getTime() + 15 * 60000 // 15 minutes from now
    }
    sessionStorage.setItem(key, JSON.stringify(item));
  }

The method to retrieve items :

private getSessionData<T>(key: string): T | null {
    const itemStr = sessionStorage.getItem(key);
    if(!itemStr) return null;
    const item = JSON.parse(itemStr);
    const now = new Date();
    if (now.getTime() > item.expiry) {
      sessionStorage.removeItem(key);
      return null;
    }
    return item.value;
  }

In my controller, I access one of those value by putting them on an Observable like :

const value$ = of(this.storageService.getSessionData<string>('value'))
        .pipe(
          catchError(err => {
            console.log('The session is expired');
            // ... open a modal and route the user to the home
            return err;
          })
        );

And then i display it in my view with :

{{(value$ | async)}}

The thing I want as soon as my Stored data is expired, is to open a Modal and route the user to an other page (It is what i did in the catchError().

The problem is that when my data actually expire, in the console I have multiple logs meaning i entered multiple times in my catchError() which is a problem since my openModal function is not idempotent (so it open multiple times leading to a poor user experience) :

console.log :

The session is expired
The session is expired
The session is expired
The session is expired
The session is expired

How can i change this to actually call the error function only once ?

I tried to use throws new Error('session expired') instead of the return null; in my retrieve data function and put a try() catch() block in my controller, but it ended the same.

Does anyone got an idea on how catch my error only once ?


Solution

  • If you have multiple value$ | async in the template, you will end up with multiple subscriptions!!!

    You can apply the share() operator to ensure only a single subscription is used:

    const value$ = of(this.storageService.getSessionData<string>('value')).pipe(
      share(), 
      catchError(err => {
        console.log('The session is expired');
      })
    );