I have an Angular app and NgRx and Nx by Nrwl. Nx provides a few "Data Persistence" functions that help you fetch, update optimistically and pessimistically, and handle navigation, but I am not sure why I would use them instead of ordinary pipes.
Here's an example on StackBlitz showing some effects with optimisticUpdate
and pessimisticUpdate
alongside other effects that do the same thing without them.
The question is - what advantage do these Nx functions provide?
For reference, here's the effects file from the StackBlitz:
@Injectable()
export class CarsEffects {
updateCarOptimistic = createEffect(() =>
this.actions.pipe(
ofType(CarsActions.updateCarOptimistic),
switchMap(({ selected, oldSelected, mockError }) =>
this.carsService.updateCar(selected, mockError).pipe(
map(() => CarsActions.updateCarOptimisticSuccess()),
catchError(error => {
this.store.dispatch(
CarsActions.updateCarOptimisticFailure({ oldSelected, error })
);
return of(null);
})
)
)
)
);
updateCarOptimisticWithNx = createEffect(() =>
this.actions.pipe(
ofType(CarsActions.updateCarOptimisticWithNx),
optimisticUpdate({
run: ({ selected, mockError }) =>
this.carsService
.updateCar(selected, mockError)
.pipe(map(() => CarsActions.updateCarOptimisticWithNxSuccess())),
undoAction: ({ oldSelected }, error) =>
CarsActions.updateCarOptimisticWithNxFailure({ oldSelected, error })
})
)
);
updateCarPessimistic = createEffect(() =>
this.actions.pipe(
ofType(CarsActions.updateCarPessimistic),
switchMap(({ selected, mockError }) =>
this.carsService.updateCar(selected, mockError).pipe(
map(() => CarsActions.updateCarPessimisticSuccess({ selected })),
catchError(error =>
of(CarsActions.updateCarPessimisticFailure({ error }))
)
)
)
)
);
updateCarPessimisticWithNx = createEffect(() =>
this.actions.pipe(
ofType(CarsActions.updateCarPessimisticWithNx),
pessimisticUpdate({
run: ({ selected, mockError }) =>
this.carsService
.updateCar(selected, mockError)
.pipe(
map(() =>
CarsActions.updateCarPessimisticWithNxSuccess({ selected })
)
),
onError: (_, error) =>
CarsActions.updateCarPessimisticWithNxFailure({ error })
})
)
);
constructor(
private actions: Actions,
private store: Store,
private carsService: CarsService
) {}
}
NOTE: I don't mean for this to be a question about matters of opinion on preferable syntax; just wanting to make my code as performant, secure, and clean as possible, and would like to use these functions if they are doing something behind the scenes I am unaware of.
Yes, if you provide an id
selector function. Check out the examples here. In my own testing, if you dispatch an action multiple times, every request will go through unless you use the id
selector. Once you add that in, fetch
will cancel any existing network requests if a new action comes through with the same ID.
This functionality can be obtained with a simple switchMap
operator, but the fetch
utility has another advantage that switchMap
doesn't provide: dispatching an action with a different ID while a network request is in progress will not cancel the current request.
Consider the following example:
this.store.dispatch(getUser('AAA'));
this.store.dispatch(getUser('BBB'));
this.store.dispatch(getUser('AAA'));
With switchMap
, only the last request would go through, so you would never successfully fetch user BBB
. With fetch
, however, the last two requests would go through.