Reputation: 2587
I'm using Angular NgRX. I am stilling learning NgRX. In the code below. I'm using a selector to get the state slice from the store. If the data is undefined or the length is equal to zero then call dispatch which will then goto the DB to get the data.
My question is: Is this the right way to do this? Or should NgRX Effects handle the logic of going to the API if the store slice is undefined?
In the code below, if I don't subscribe to the selector and check it then it will call the API end-point every time, which defeats the purpose of using a state store, yeah?
import { Component, OnInit, OnDestroy } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { Observable, Subscription } from 'rxjs';
import { tap } from 'rxjs/operators';
...
@Component({
selector: 'my-pro',
templateUrl: './my-pro.component.html',
styleUrls: ['./my-pro.component.scss'],
})
export class MyProxyComponent implements OnInit, OnDestroy {
myProxyList$ = this.store.select(getProxyList);
subs: Subscription;
searchCriteria = {
standardId: 'mka',
start: 1,
end: 25,
};
constructor(
private store: Store<State>,
) {
}
ngOnInit(): void {
this.getMyProxy();
}
ngOnDestroy(): void {
this.subs.unsubscribe();
}
getMyProxy(): void {
this.subs = this.myProxyList$.subscribe(
data => {
console.log('myProxyList', data);
// if length is 0, goto the db
if (data === undefined || data.length === 0) {
// ngrx action to call api
this.store.dispatch(ProxyActions.getAllProxyList({ searchCriteria: this.searchCriteria }));
}
}
);
}
}
Upvotes: 1
Views: 627
Reputation: 327
David Rinck's answer works perfect for me. Just add a full code example based on it:
fetchItems$ = createEffect((
action$ = inject(Actions),
service = inject(YourService),
store = inject(Store)
) => action$.pipe(
ofType(YourActions.fetchItems),
withLatestFrom(store.select(YourFeature.selectItems)),
filter(([, state]) => {
// change to your own empty predication
return state.data.length === 0;
}),
exhaustMap(() => service.fetchAll().pipe(
map((data) => YourActions.fetchItemsSuccess({ data })),
catchError(({ error }: HttpErrorResponse) => of(
YourActions.fetchItemsFailure({
error
})
)),
)),
), { functional: true }
)
Upvotes: 0
Reputation: 6996
You definitely want to do this in your Effects. It is a lot of boiler plate. If it seems like too much, look in to @ngrx/component-store or the rxjs facade. They have less indirection, and in your use case, especially for just one component, they may be a better fit. See for example, the search-tickets example here: https://medium.com/angular-in-depth/angular-you-may-not-need-ngrx-e80546cc56ee
For the ngrx solution, add a proxyListLoaded
boolean in your Store to track if it's already been loaded.
proxyList$ = createEffect(() =>
this.actions$.pipe(
ofType(proxyActions.getAllProxyList),
withLatestFrom(
this.store.select(getProxyListLoaded),
),
filter(([_action, loaded]) => !loaded),
exhaustMap(() =>
this.proxyService.getProxyList().pipe(
map(result => new LoadOrders(result))
))
);
Upvotes: 2