ngrx仅在存储为空时从服务器加载数据

时间:2017-10-12 08:55:55

标签: angular redux ngrx

我有一组静态数据,一组国家/地区,用于某些组件。这些数据加载到ngOnInit()这些组件上,但我只是在我第一次请求数据(商店为空)时加载它们。随后我每次加载组件时都希望简单地使用商店中的数据而不需要刷新"它

如何使用ngrx实现这一目标?

我正在使用效果。这就是我的代码的样子:

组件:

export class EditPageComponent implements OnInit {
countries$: Observable<Country[]>

constructor(private store: Store<fromContacts.State>) {
    this.countries$ = store.select(fromContacts.getCountriesEntities);
}
ngOnInit() {
   this.store.dispatch(new countries.Load());
}

效果:

    @Effect()
      loadCollection$: Observable<Action> = this.actions$
        .ofType(countries.LOAD)
        .switchMap(() =>
          this.countriesService
        .getCountries()
        .map((countriesList: Country[]) => {
          return new countries.LoadSuccess(countriesList);
        })
        .catch(error => of(new countries.LoadFail(error)))
  );

还原剂:

case countries.LOAD_SUCCESS: {

      const countriesList: Country[] = action.payload;
      const reducedCountries: { [id: string]: Country } = countriesList.reduce((countrs: { [id: string]: Country }, countr: Country) => {
        return Object.assign(countrs, {
            [countr.code]: countr
        });
    }, {});

谢谢, 加布

5 个答案:

答案 0 :(得分:8)

有不同的方法可以做到这一点。首先,您可以在州内保留hasLoaded: boolean财产。然后,您可以在拨打服务电话之前进行检查。

ngOnInit() {
  this.store.select(getHasLoaded)
    .take(1)
    .subscribe(hasLoaded => {
      if (!hasLoaded) this.store.dispatch(new countries.Load()); 
    }
}

另一个选择是让你的@Effect检查hasLoaded属性:

@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)))
);

为此,您需要在Effects构造函数中提供商店。

答案 1 :(得分:3)

TL; DR

在效果中使用take(1)运算符

  

不要忘记使用catchError进行错误处理并返回EMPTY,否则,当发生错误时,该错误将始终返回(超时,身份验证错误,离线...)


我的情况与您完全相同,我所做的只是添加了rxjs运算符take的效果,以便仅在第一次执行LoadCountries动作时才获取国家/地区。

@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)
  );

EMPTY内部返回catchError将完成可观察的过程,而无需通过take(1)

答案 2 :(得分:1)

您可以从效果中的商店中选择国家,如果它们在商店中有代表,我们将忽略操作,否则,我们将获取国家/地区。

@Effect()
getOrder = this.actions.pipe(
  ofType<GetOrder>(ActionTypes.GetOrder),
  withLatestFrom(this.store.pipe(select(getOrders))),
  filter(([{payload}, orders]) => !!orders[payload.orderId])
  mergeMap([{payload}] => {
    ...
  })
)

有关更多信息,请参见Start using ngrx/effects for this

答案 3 :(得分:0)

我的答案与Hetty de Vries答案有所不同。 如果您只是想在不影响效果的情况下检索商店的内容,则可以执行以下操作:

this.store.select(state => state.producer.id).forEach( id => {
...
}

假设您的商店包含一个producer对象,该对象的属性为id

答案 4 :(得分:0)

在效果中使用withLatestFrom

另外,检查 || products.length === 1,当用户登陆单个产品页面时会发生这种情况。

  loadProducts$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(loadProducts),
      withLatestFrom(this.store.select(getProducts)),
      mergeMap(([action, products]) => {
        if (!products.length || products.length === 1) {
          ...
          return this.productsService.getProducts().pipe(
            map((products) => {
              ...
              return loadProductsSuccess({ products });
            }),
            catchError((error) => {
              ...
              return of(errorAction());
            })
          );
        }
        return of(dummyAction());
      })
    );
  });