angularrxjsrxjs-pipeable-operators

RxJS cache and refresh with shareReplay


I am using a caching for some data retrieved from an API, for logical reasons the stored data is valid only for a limited time, so I am making use of something like:

someApiData$ = this.getData()
    .pipe(shareReplay(1, 3000))

What seems to be obvious to me but apparently is not to the creator of the shareReplay operator is that if the data is no longer cached it should be re-fetched, or at the very least I should have another parameter that will give me this option, something like:

someApiData$ = this.getData()
    .pipe(shareReplay(1, 3000, shouldRefresh))

Instead, what the next subscriber will get is null. So, I am looking for an elegant solution to this issue.


Solution

  • After some trails with the answers on this thread and some other approaches on the web, this is what I ended up with. It gives the ability to:

    1. Cache values
    2. Refresh the values automatically if the data is no longer cached
    3. Work directly with the Observable
    4. Specify the duration of the cache lifetime if needed
    5. Unclutter my services and provide a reusable solution

    My caching util:

    export class SharedReplayRefresh {
    
        private sharedReplay$: Observable<T>;
        private subscriptionTime: number;
    
    
       sharedReplayTimerRefresh(
           source: Observable<T>, bufferSize: number = 1,
           windowTime: number = 3000000,  scheduler?: SchedulerLike): Observable<T> {
    
            const currentTime = new Date().getTime();
            if (!this.sharedReplay$ || 
                currentTime - this.subscriptionTime > windowTime) {
                this.sharedReplay$ = source.pipe(shareReplay(
                    bufferSize, windowTime, scheduler));
                this.subscriptionTime = currentTime;
            }
    
            return this.sharedReplay$;
        }
    }
    

    My data-service:

    export class DataService {
        
        constructor(private httpClient: HttpClient) { }
    
        private dataSource = 
            new SharedReplayRefresh<Data>();
        private source = this.httpClient.get<Data>(url);
        
        get data$(): Observable<Data> {
            return this.dataSource .sharedReplayTimerRefresh(this.source, 1, 1500);
        }
    }