web-dev-qa-db-ja.com

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
        });
    }, {});

ありがとう、ガブ

14
gabric

これにはさまざまな方法があります。まず最初に、hasLoaded: boolean状態のプロパティ。その後、サービスのget呼び出しを行う前にこれを確認できます。

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コンストラクターにストアを提供する必要があります。

9
Hetty de Vries

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)を経由せずにオブザーバブルが完了します

ストア内で国が選択されている場合は、それらがストアに表示されている場合はアクションを無視し、そうでない場合は国をフェッチします。

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

詳細については、 このためのngrx/effectsの使用を開始 を参照してください。

4
timdeschryver

私の答えはHetty de Vries回答。エフェクトに触れずに単にストアのコンテンツを取得したい場合は、次のようなことができます。

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

ストアにproducer属性を持つidオブジェクトが含まれているとします。

0
Investigator