How prevent loading data second time in NgRx?

Viewed 383

I have a DOM block:

<div (click)="toggle(block)">
   <div *ngIf="block.opened">
     <div *ngFor="let item of block.items"></div>
   </div>
</div>

By click I dispatch action to load data from server and display this data in block:

  toggle(block: RegistryGroup) {
    this.store.dispatch(RegistryActions.ToggleRegistryBlockAction(block));
    this.store.dispatch(RegistryActions.LoadRegistryLayersAction(this.block));
  }

First event ToggleRegistryBlockAction changes property of block to true/false. The second LoadRegistryLayersAction makes request to the server by block.id and returns data to the same state of block to the property block.items = Server Response;

In effect side where I listen action LoadRegistryLayersAction:

loadRegistriesLayers$: Observable<Action> = createEffect(() =>
    this.actions$.pipe(
      ofType(RegistryActions.LoadRegistryLayersAction),
      filter(
        (registryGroup) =>
          registryGroup.items && !registryGroup.items.length
      ),
      switchMap((registryGroup: RegistryGroup) =>
        from(this.registry.getRegistryLayers(registryGroup.Id)).pipe(
          map((layers: { [key: string]: RegistryLayerItemGeneric[] }) => {
            return RegistryActions.SuccessLoadRegistryLayersAction({
              payload: {
                layers: this.registry.createLayersMapWithObjects(layers),
                registryGroupId: registryGroup.Id,
              },
            });
          }),
          catchError((error: Error) => {
            return of(RegistryActions.ErrorRegistryLayersAction(error));
          })
        )
      )
    )
  );

I filter action and check if data already present. For that I check if block has registryGroup.items && !registryGroup.items.length.

Problem is that sometimes after execution this effects server can return empty []. So next time, when user requests it, it sends request repeatly.

How to fix it and dont touch server if data was loaded?

2 Answers

There are multiple solutions:

  • keep a status property which can be loading, loaded, empty,...
  • keep a list of id's that are fetched in the state, and check this list before sending the request
  • don't check on registryGroup.items.length, and initialize the array as null

Try the ngrx-loading-state lib:

https://github.com/amzhang/ngrx-loading-state

Live demo to avoid issuing API load multiple times:

https://stackblitz.com/edit/angular-ivy-rocjzb?file=src%2Fapp%2Fapp.component.ts

You you are after is to use the NgRx store as a cache. So somewhere we need to track the age or the presence of the last successful load. The above lib does the heavy lifting of deciding if an API call is necessary based on a maxAge parameter. Look for the filterLoading() function in the effects. That's where the checking happens.

It's boilerplatey, NgRx always is. But it offers consistency and a pattern to follow.

Related