ngrx Load data from server only if the store is empty
There are different ways of doing this. First of all you can keep a hasLoaded: boolean
property in the state. Then you can check this before you make the service get call.
ngOnInit() { this.store.select(getHasLoaded) .take(1) .subscribe(hasLoaded => { if (!hasLoaded) this.store.dispatch(new countries.Load()); }}
Another option is to let your @Effect check the hasLoaded property:
@Effect() loadCollection$: Observable<Action> = this.actions$ .ofType(countries.LOAD) .withLatestFrom(this.store.select(getHasLoaded) .filter(([ action, hasLoaded ]) => !hasLoaded) // only continue if hasLoaded is false .switchMap(() => this.countriesService .getCountries() .map((countriesList: Country[]) => { return new countries.LoadSuccess(countriesList); }) .catch(error => of(new countries.LoadFail(error))));
For this to work you need to provide the store in your Effects constructor.
TL;DR
Use take(1)
operator in the effect
Don't forget the error handling using
catchError
and returnEMPTY
, otherwise when an error occurs, that error will be returned always (timeout, auth error, offline...)
I had exactly the same case as you, what I did was adding in the effects the rxjs operator take
to fetch the countries only the first time the LoadCountries
action was dispatched.
@Effect() loadCountries$: Observable<CoreActions> = this.actions$.pipe( ofType(CoreActionTypes.LoadCountries), mergeMap(() => this.countriesService.getAllCountries().pipe( map(c => new LoadCountriesSuccess(c)), catchError(() => { this.store.dispatch(new LoadCountriesFailed()); return EMPTY; }) ) ), take(1) );
Returning EMPTY
inside catchError
will complete the observable without passing through the take(1)
You can select the countries from the store within effects, if they are represented in the store we ignore the action, if not we fetch the countries.
@Effect()getOrder = this.actions.pipe( ofType<GetOrder>(ActionTypes.GetOrder), withLatestFrom(this.store.pipe(select(getOrders))), filter(([{payload}, orders]) => !!orders[payload.orderId]) mergeMap([{payload}] => { ... }))
For more info see Start using ngrx/effects for this.