如何使用@ngrx / store获取State对象的当前值?

时间:2016-02-25 17:03:15

标签: angular rxjs5 ngrx

我的服务类在调用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

12 个答案:

答案 0 :(得分:53)

@ ngrx / store v1.x

的原始答案

@ngrx/store扩展BehaviorSubject,并且您可以使用value属性。

this._store.value

这将是您应用的当前状态,从那里您可以选择属性,过滤器,地图等......

<强>更新

我花了一些时间来确定你的例子中的内容(:要获得dataForUpdate的当前值,你可以使用:

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

@ ngrx / store v2.x

的更新

随着版本2的更新,value已删除subscribe()

  

已删除用于从Store中同步提取最新状态值的API。相反,如果必须获取状态值,则可以始终依赖function getState(store: Store<State>): State { let state: State; store.take(1).subscribe(s => state = s); return state; } 同步运行:

/tmp/imagenet/classify_image_graph_def.pb

答案 1 :(得分:7)

订阅链中的

withLatestFrom()combineLatest()方法可以满足您的需求,并与Observables + Ngrx的精神保持一致。

代替上面代码中的GET STATE .mergeMap(),使用withLatestFrom()看起来像这样:

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

顺便说一下,原始问题中的代码似乎是在管理redux操作的异步效果,这正是ngrx/effects库的用途。我建议你看看。在连接了Effects之后,用于管理异步redux操作的代码更加清晰。吉姆林奇的这篇文章对我也很有帮助:The Basics of "ngrx/effects", @Effect, and Async Middleware for "ngrx/store" in Angular 2

答案 2 :(得分:6)

并非严格地直接回答问题,但我发现此页面正在寻找如何从商店中检索单个值。

为实现此目的,您可以从State注入@ngrx/store对象,如下所示:

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

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

state对象将当前状态保存在私有_value属性中,由.getValue()方法访问。

答案 3 :(得分:5)

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

答案 4 :(得分:1)

我创建了一个简约应用程序,其状态包含2个计数器,这些计数器是AppState的属性,还有2个reducer。每个reducer都绑定到一个特定的计数器,我为每个计数器订阅了一个console.log值的观察者。减速器本身也会在调用时写入控制台。

有一个按钮通过调度事件来调用两个减速器。此外,2个计数器绑定到2个标签,因此其中的更改显示为 - <p>Counter: {{counter1 | async}}</p>

使用StoreModule.forRoot({ counter1: Reducer1, counter2 : Reducer2 })

将每个计数器映射到reducer
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 { }

答案 5 :(得分:0)

我的解决方案

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

答案 6 :(得分:0)

这对我有用。您需要从“ @ ngrx / store”导入Store,并且AppState是您的状态。

private state: AppState;

constructor(private store: Store<AppState>) { }

ngOnInit() {
    this.store.select(x => this.state = x).subscribe();
}

答案 7 :(得分:0)

@ ngrx / store v4.x的更新

从v4.x开始,我们不得不像这样将take运算符放入管道中,以使其同步:

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

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

    return state;
}

答案 8 :(得分:0)

要从商店同步获取值,您必须订阅商店,但不是将订阅存储在 Observable 中,您只需将其传递给变量。您也可以使用选择器来执行此操作。

let id: string;
this.store.select(fromWhatever.selectId).subscribe(i => id = i);

答案 9 :(得分:0)

这只是我对这个问题的经验,而不是标准的 code

请看github中的答案: State snapshot #227

我想在 state 中获得 constractor,所以我习惯于 unasynchronous

constructor (private state: State<AppState>) {

   this.store.select("yourSelector").forEach(yourSelector => { 
       this.property = yourSelector.path.to.state.property 
   });

}

答案 10 :(得分:-1)

额外评论。当我使用this._store.value.StateReducer.currentPeriod.id

Transpiler return“app / state / stateService.ts(133,35):error TS2339:'AppState'类型中不存在属性'StateReducer'。”

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





}

要解决问题,我更改了rxjs \ subject文件夹中的BehaviorSubject.d.ts:

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

不确定这是否是合法修改;)

答案 11 :(得分:-2)

这对我有用。我将获取我的对象数据。

sig