web-dev-qa-db-ja.com

@ ngrx / storeでStateオブジェクトの現在の値を取得する方法は?

私のサービスクラスは、Webサービスを呼び出す前に、私の状態からdataForUpdateというプロパティを取得する必要があります。現在、私はこれを次のようにしています:

constructor ( public _store: Store<AppState>, 
              public _APIService:APIService) {

    const store$ =  this._store.select ('StateReducer');

    .../...

    let update = this.actions$.filter ( action => action.type==UPDATE )
                              .do( (action) => this._store.dispatch({type: REDUCER_UPDATING, payload : action.payload  }) )
      *** GET STATE ***==>    .mergeMap ( action => store$.map ( (state : AppState)=> state.dataForUpdate ).distinctUntilChanged(),
                                         (action, dataForUpdate) { 
                                                   return { type:action.type, payload : {employee:action.payload, dataForUpdate :dataForUpdate }    }; 
                                                            })
       * AND CALL API *==>    .mergeMap ( action =>this._APIService.updateEmployee(action.payload.employee, action.payload.dataForUpdate),
                                         (action, APIResult) => { return  { type:REDUCER_UPDATED }})
                              .share ();


    .../...

    let all = Observable.merge (update, ....);
    all.subscribe ((action:Action) => this._store.dispatch (action));

}

として、angular2-store-example( https://github.com/ngrx/angular2-store-example/blob/master/src/app/users/models/users.ts )を使用していますフォローするガイド。

より良い(よりきれいな)方法が存在するのだろうか?

ライブの例: https://plnkr.co/edit/WRPfMzPolQsYNGzBUS1g?p=preview

37
Philippe sillon

@ ngrx/store v1.xの元の回答

@ngrx/storeBehaviorSubject を拡張し、使用できるvalueプロパティがあります。

this._store.value

それがアプリの現在の状態になり、そこからプロパティ、フィルター、マップなどを選択できます...

更新:

あなたの例の内容を理解するのにしばらくかかりました(:dataForUpdateの現在の値を取得するには、以下を使用できます:

let x = this._store.value.StateReducer.dataForUpdate;
console.log(x); // => { key: "123" }

@ ngrx/store v2.xの更新

バージョン2への更新により、 docs で説明されているようにvalueが削除されました。

Storeから最新の状態値を同期的にプルするためのAPIは削除されました。代わりに、状態値を取得する必要がある場合は、subscribe()が常に同期して実行されることに依存できます。

function getState(store: Store<State>): State {
    let state: State;

    store.take(1).subscribe(s => state = s);

    return state;
}
54
Sasxa

サブスクリプションチェーンのwithLatestFrom()またはcombineLatest()メソッドは、必要なものだけを提供し、Observables + Ngrxの精神に沿っています。

上記のコードのGET STATE .mergeMap()の代わりに、withLatestFrom()を使用すると次のようになります。

...
.withLatestFrom(store$, (payload, state) => { 
    return {payload: payload, stateData: state.data} 
} )
...

余談ですが、元の質問のコードはreduxアクションの非同期効果を管理しているように見えます。これはまさに ngrx/effects ライブラリの目的です。ぜひチェックしてみてください。 Effectsの接続が完了すると、非同期reduxアクションを管理するためのコードはずっときれいになります。 Jim Lynchによるこの記事は、私にとっても非常に役に立ちました。 [ngrx/effects]、@ Effect、およびAngularの「ngrx/store」の非同期ミドルウェアの基本2

7

厳密には質問への直接的な答えではありませんが、ストアから単一の値を取得する方法を探しているこのページを見つけました。

これを実現するために、以下に示すように@ngrx/storeからStateオブジェクトを注入できます。

import { State } from '@ngrx/store';

constructor (private state: State<AppState>) {
    let propertyValue = state.getValue().path.to.state.property;
}

stateオブジェクトは、.getValue()メソッドによってアクセスされるプライベート_valueプロパティに現在の状態を保持します。

7

@Sasxaからの回答に続いて、@nrgx/storeの新しいバージョン(v5およびv6)で構文が変更されました。基礎となるRxJSライブラリが^ 5.5.0に更新された後、すべてのObservableインスタンスで使用可能なパイプメソッドがあり、これにより、チェーンを容易にし、サブスクリプションの達成方法を変更できます。

したがって、次のようなことができます。

import { take } from 'rxjs/operators';

store.select('your-state').pipe(take(1)).subscribe(
    val => console.log(val)
);

または、厳密にpipe()演算子を使用します:

import { select } from '@ngrx/store';
import { take } from 'rxjs/operators';

store.pipe(select('your-state'), take(1)).subscribe(
    val => console.log(val)
);
7
carsanlop

AppStateのプロパティである2つのカウンターと2つのリデューサーを持つ状態を持つミニマルなアプリケーションを作成しました。各レデューサーは特定のカウンターにバインドされており、console.logその値になる各カウンターのオブザーバブルをサブスクライブしました。リデューサー自体も、呼び出されたときにコンソールに書き込みます。

イベントをディスパッチすることで両方のリデューサーを呼び出すボタンがあります。また、2つのカウンターは2つのラベルにバインドされているため、それらの変更は-<p>Counter: {{counter1 | async}}</p>と表示されます。

各カウンターをレデューサーにマッピングするには、StoreModule.forRoot({ counter1: Reducer1, counter2 : Reducer2 })を使用します

import { Component, NgModule } from '@angular/core';
import { Store, Action, StoreModule } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { BrowserModule } from '@angular/platform-browser';

interface AppState {
  counter1 : number;
  counter2 : number;
}

export function Reducer1(counter : number = 0, action : Action) {
  console.log(`Called Reducer1: counter=${counter}`);
  return counter + 1;
}

export function Reducer2(counter : number = 0, action : Action) {
  console.log(`Called Reducer2: counter=${counter}`);
  return counter + 2;
}

@Component({
  selector: 'app-root',
  template: `<p>Counter: {{counter1 | async}}</p>
  <p>Counter: {{counter2 | async}}</p>
  <button (click)='increment()'>Increment</button>`
})
export class AppComponent {
  title = 'app';
  counter1 : Observable<number>;
  counter2 : Observable<number>;

  constructor(private store : Store<AppState>) {
    this.counter1 = this.store.select('counter1');
    this.counter2 = this.store.select('counter2');

    this.counter1.subscribe(x => console.log(`Subscribe event for counter1 fired: counter=${x}`));
    this.counter2.subscribe(x => console.log(`Subscribe event for counter2 fired: counter=${x}`));
  }

  increment() {
    this.store.dispatch({type:'foo'});
  }
}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    StoreModule.forRoot({ counter1: Reducer1, counter2 : Reducer2 })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
1
sashoalm

私のソリューション


ngStoreのStateクラスはBehaviorSubjectであるため、これを注入し、そのvalueプロパティを使用して最新の値を取得できます。

constructor(private state:State<YourState>...) {

}

someMethod() {
    // WHAT'S MORE: you can use your selector directly on it!
    let v = yourSelector(this.state.value);
}
1
Jason Song