I'm having hard time finding neet solution for cashing http request to BehaviorSubject. I wanted to have only single method for cashing and getting value. Example below:
@Injectable({
providedIn: 'root'
})
export class DataStoreService {
private readonly initialState: DataState = {
data: string,
loaded: false
};
private data$: BehaviorSubject<DataState> = new BehaviorSubject<DataState>(this.initialState);
constructor(private dataService: DataService) { }
getData$(id: string): Observable<string> {
return this.data$.asObservable()
.pipe(
switchMap((state: DataState) => {
if (!state.loaded) {
this.dataService.getData$(id);
} else {
return of(state.data);
}
}),
withLatestFrom(this.data$.asObservable()),
map(([data, state]) => {
if (!state.loaded) {
this.data$.next({
loaded: true,
data
});
}
return data;
})
);
}
}
I haven't tested it yet (service not working), but looks ok for now. Anybody knows any better solution to have less piping and conditionals (if/else) in rxjs for this kind of usage?
Instead of a BehaviorSubject
you could use the operator shareReplay
which uses a ReplaySubject
internally and replays the n most recent values to future subscribers.
private data$: Observable<string>;
getData$(id: string): Observable<string> {
if (!this.data$) {
this.data$ = this.dataService.getData$(id).pipe(shareReplay(1));
}
return this.data$;
}
The first subscription to getData$
will call this.dataService.getData$
. All later subscriptions will get the data from that first call replayed by data$
.