How to get current value of State object with @ngrx/store?

笑着哭i 提交于 2019-11-28 05:17:12

Original answer for @ngrx/store v1.x

@ngrx/store extends BehaviorSubject and it has a value property you can use.

this._store.value

that will be the current state of your app, and from there you can select properties, filter, map etc...

update:

Took me a while to figure what's what in your example (: To get current value of dataForUpdate, you can use:

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

Update for @ngrx/store v2.x

With the update to version 2, value was removed as described in docs:

The APIs for synchronously pulling the most recent state value out of Store have been removed. Instead, you can always rely on subscribe() running synchronously if you have to get the state value:

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

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

    return state;
}

Not strictly a direct answer to the question, but I found this page looking for how to retrieve a single value from the store.

To achieve this, you can inject the State object from @ngrx/store as shown below:

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

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

The state object holds the current state in a private _value property, accessed by the .getValue() method.

Matthew Marichiba

withLatestFrom() or combineLatest() methods in the subscription chain give you just what you need, and are aligned with the spirit of Observables+Ngrx.

In place of the GET STATE .mergeMap() in the code above, using withLatestFrom() would look something like this:

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

As an aside, the code in the original question appears to be managing asynchronous effects of redux actions, which is exactly what the ngrx/effects library is for. I suggest you check it out. After you get Effects wired up, the code for managing asynchronous redux actions is much cleaner. This article by Jim Lynch was also super helpful to me: The Basics of "ngrx/effects", @Effect, and Async Middleware for "ngrx/store" in Angular 2

Following the answer from @Sasxa, the syntax changed on newer versions of @nrgx/store (v5 and v6). After the underlying RxJS library was updated to ^5.5.0, there is now a pipe method available on all the Observable instances, which allows for easier chaining and changes how a subscription is achieved.

So you can now do something like:

import { take } from 'rxjs/operators';

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

Or, using strictly the pipe() operator:

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

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

I've created a minimalistic application that has a state with 2 counters which are properties of the AppState, and 2 reducers. Each reducer is bound to a particular counter, and I've subscribed an observable for each counter that will console.log its value. The reducers themselves also write to the console when called.

There is a button which calls both reducers by dispatching an event. Also, the 2 counters are bound to 2 labels, so changes in them show - <p>Counter: {{counter1 | async}}</p>.

Mapping each counter to a reducer is done with 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 { }

My Solution


the State class in ngStore is an BehaviorSubject, so we can inject it, and use it's value property to get the latest value.

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

}

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

Extra comment. When I use this._store.value.StateReducer.currentPeriod.id

Transpiler return "app/state/stateService.ts(133,35): error TS2339: Property 'StateReducer' does not exist on type 'AppState'."

constructor ( public _store: Store<AppState>) {

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

    .../...


    let saveTransaction = this.actions$
                  .filter (action => action.type==SAVE_TRANSACTION )
                  .map (action => { return { type:SAVING_TRANSACTION, payload : action.payload };  } )
                  .mergeMap ( action => this._transactionService.updateTransaction (
                                                            this._store.value.StateReducer.currentProfile.id, 
                                                            this._store.value.StateReducer.currentPeriod.id, 
                                                            action.payload),
                                (state, webServiceResponse) =>  { return { type:TRANSACTION_UPDATED, payload :null  }; }) ;





}

To fix issue, I have changed BehaviorSubject.d.ts in rxjs\subject folder :

import { Subject } from '../Subject';
import { Subscriber } from '../Subscriber';
import { Subscription } from '../Subscription';
export declare class BehaviorSubject<T> extends Subject<T> {
    private _value;
    private _hasError;
    private _err;
    constructor(_value: T);
    getValue(): T;        
    value: T;             <=== I have changed it to value: any;
    _subscribe(subscriber: Subscriber<any>): Subscription<T>;
    _next(value: T): void;
    _error(err: any): void;
}

Not sure if it's a legit modification ;)

this is work for me. i will get my object data.

this.store.select('dataStore').subscribe(data => { 
          console.log(data)
 }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!