web-dev-qa-db-fra.com

Comment obtenir la valeur actuelle de l'objet State avec @ ngrx/store?

Avant d'appeler un service Web, ma classe de service doit obtenir une propriété appelée dataForUpdate de mon état. Actuellement, je le fais comme ça:

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

}

J'utilise angular2-store-example ( https://github.com/ngrx/angular2-store-example/blob/master/src/app/users/models/users.ts ) comme guide pour suivre.

Je me demande s'il existe une meilleure méthode (plus propre)?

Exemple en direct: https://plnkr.co/edit/WRPfMzPolQsYNGzBUS1g?p=preview

32
Philippe sillon

Réponse originale pour @ ngrx/store v1.x

@ngrx/store extend BehaviorSubject et il possède une propriété value que vous pouvez utiliser.

this._store.value

ce sera l'état actuel de votre application, et à partir de là, vous pourrez sélectionner des propriétés, filtrer, mapper, etc.

mettre à jour:

Il m'a fallu un certain temps pour comprendre ce que votre exemple donne (: Pour obtenir la valeur actuelle de dataForUpdate, vous pouvez utiliser:

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

Mise à jour pour @ ngrx/store v2.x

Avec la mise à jour vers la version 2, value a été supprimé comme décrit dans docs :

Les API permettant d'extraire de manière synchrone la valeur d'état la plus récente de Store ont été supprimées. Au lieu de cela, vous pouvez toujours compter sur subscribe() s'exécutant de manière synchrone si vous devez obtenir la valeur d'état:

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

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

    return state;
}
52
Sasxa

Les méthodes withLatestFrom() ou combineLatest() de la chaîne d'abonnement vous donnent exactement ce dont vous avez besoin et sont alignées sur l'esprit d'Observables + Ngrx.

A la place de la fonction GET STATE .mergeMap() dans le code ci-dessus, utiliser withLatestFrom() ressemblerait à ceci:

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

En passant, le code de la question initiale semble gérer les effets asynchrones des actions de redux, ce à quoi sert exactement la bibliothèque ngrx/effects . Je vous suggère de vérifier. Une fois les effets connectés, le code permettant de gérer les actions de redux asynchrones est beaucoup plus propre. Cet article de Jim Lynch m'a aussi beaucoup aidé: Les bases de "ngrx/effects", @Effect et Async Middleware pour "ngrx/store" dans Angular 2

7
Matthew Marichiba

Ce n’est pas strictement une réponse directe à la question, mais j’ai trouvé cette page cherchant comment récupérer une valeur unique du magasin.

Pour ce faire, vous pouvez injecter l'objet State à partir de @ngrx/store comme indiqué ci-dessous:

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

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

L'objet state contient l'état actuel dans une propriété privée _value, accessible par la méthode .getValue().

6
Alex Currie-Clark

Suite à la réponse de @Sasxa, la syntaxe a été modifiée pour les versions plus récentes de @nrgx/store (v5 et v6). Une fois que la bibliothèque RxJS sous-jacente a été mise à jour à ^ 5.5.0, une méthode de canal est désormais disponible sur toutes les instances Observable, ce qui facilite le chaînage et modifie la manière dont un abonnement est réalisé.

Donc, vous pouvez maintenant faire quelque chose comme:

import { take } from 'rxjs/operators';

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

Ou, en utilisant strictement l'opérateur pipe():

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

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

J'ai créé une application minimaliste qui a un état avec 2 compteurs qui sont des propriétés de l'AppState et 2 réducteurs. Chaque réducteur est lié à un compteur particulier et j'ai souscrit un observable pour chaque compteur qui console.log sa valeur. Les réducteurs eux-mêmes écrivent également sur la console lorsqu'ils sont appelés.

Il existe un bouton qui appelle les deux réducteurs en envoyant un événement. De plus, les 2 compteurs sont liés à 2 étiquettes, leur modification indique donc - <p>Counter: {{counter1 | async}}</p>.

Le mappage de chaque compteur sur un réducteur se fait avec 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 { }
0
sashoalm

Ma solution


la classe State dans ngStore est un BehaviorSubject, nous pouvons donc l'injecter et utiliser sa propriété value pour obtenir la dernière valeur.

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

}

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