Caching requests executed by Actions is a common practice. NGXS does not provide this ability out of the box, but it is easy to implement.
There are many different ways to approach this. Below is a simple example of using the store's current values and returning them instead of calling the HTTP service.
import { State, Action, StateContext } from'@ngxs/store';import { tap } from'rxjs/operators';exportclassGetNovels {staticreadonly type ='[Novels] Get novels';}@State<Novel[]>({ name:'novels', defaults: []})exportclassNovelsState {constructor(private novelsService:NovelsService) {} @Action(GetNovels)getNovels(ctx:StateContext<Novel[]>) {returnthis.novelsService.getNovels().pipe(tap(novels =>ctx.setState(novels))); }}
Imagine that this state of novels contains only minimal information about them such as ID and name. When the user selects a particular novel - he is redirected to a page with full information about this novel. We want to load this information only once. Let's create a state and call it novelsInfo, this will be the object whose keys are the identifiers of the novels:
import { State, Action, StateContext, createSelector } from'@ngxs/store';import { tap } from'rxjs/operators';exportinterfaceNovelsInfoStateModel { [key:string]:Novel;}exportclassGetNovelById {staticreadonly type ='[Novels info] Get novel by ID';constructor(public id:string) {}}@State<NovelsInfoStateModel>({ name:'novelsInfo', defaults: {}})exportclassNovelsInfoState {staticgetNovelById(id:string) {returncreateSelector( [NovelsInfoState], (state:NovelsInfoStateModel) => state[id] ); }constructor(private novelsService:NovelsService) {} @Action(GetNovelById)getNovelById(ctx:StateContext<NovelsInfoStateModel>, action:GetNovelById) {constnovels=ctx.getState();constid=action.id;if (novels[id]) {// If the novel with ID has been already loaded// we just break the executionreturn; }returnthis.novelsService.getNovelById(id).pipe(tap(novel => {ctx.patchState({ [id]: novel }); }) ); }}
The component, that displays information about the novel, can subscribe to the params observable of the ActivatedRoute to listen to the params change. The code will look as following:
We're using the switchMap in this example, so if the user navigates to another novel and params observable emits new value - we have to complete previously started asynchronous job (in our case it's getting novel by ID).