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

问题描述 投票:0回答:12

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

angular rxjs5 ngrx
12个回答
87
投票

@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
已被删除,如 docs:

中所述

删除了从 Store 同步拉取最新状态值的 API。相反,如果您必须获取状态值,您始终可以依赖

subscribe()
同步运行:

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

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

    return state;
}

30
投票

根据 @Sasxa 的回答,较新版本的

@nrgx/store
(v5 和 v6)的语法发生了变化。在底层 RxJS 库更新到 ^5.5.0 后,现在所有
Observable
实例上都可以使用管道方法,这允许更轻松的链接并改变订阅的实现方式。

所以你现在可以做类似的事情:

import { take } from 'rxjs/operators';

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

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

   return state;
}

或者,严格使用

pipe()
运算符:

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

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

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

   return state;
}

如果您想让代码更具可读性,您还可以采用异步/等待机制,如下所示:

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

function async getStateAsync(store: Store<State>): State {
   let state = await store
             .pipe(
                select('your-state'),
                take(1)
             )
             .toPromise<State>();

   return state;
}

15
投票
订阅链中的

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 的这篇文章对我也非常有帮助:The Basics of "ngrx/effects", @Effect, and Async Middleware for "ngrx/store" in Angular 2


14
投票

严格来说不是问题的直接答案,但我发现此页面正在寻找如何从商店检索单个值。

要实现此目的,您可以从

State
注入
@ngrx/store
对象,如下所示:

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

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

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


10
投票

我的解决方案


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

5
投票

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


1
投票

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

private state: AppState;

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

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

0
投票

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

Transpiler 返回“app/state/stateService.ts(133,35):错误 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;
}

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


0
投票

我创建了一个简约的应用程序,它的状态有 2 个计数器(AppState 的属性)和 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 { }

0
投票

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

code

请看github上的答案: 状态快照#227

我想把

state
放入
constractor
,所以我习惯
unasynchronous
:

constructor (private state: State<AppState>) {

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

}

0
投票

执行此操作的一种巧妙方法是将订阅包装在承诺中,然后等待承诺。

import { Store } from '@ngrx/store';
import { AppState } from './app.state';

// Assume some action and selector are defined in your NgRx application
// const someAction = ...;
// const someSelector = ...;

// Assume this function is defined in some common location.
export const getState = (store) => {
  return new Promise((resolve, reject) => {
    // Get an observable from the selector.
    const someState$ = store.select(someSelector);

    // Set up the subscription and resolve its callback value.
    // This approach to defining the subscription lets you wire up
    // both resolve and reject handling.
    someState$.subscribe({
      next: value => {
        // Unsubscribe to prevent memory leaks
        someState$.unsubscribe();
        
        // Resolve the promise with the subscribed value
        resolve(value);
      },
      error: err => {
        // Unsubscribe and reject the promise in case of an error
        someState$.unsubscribe();
        reject(err);
      }
    });
  });
}

// ...

// Assume this is called elsewhere, such as from your angular component,
// for example.
async doSomething() {
  try {
    const result = await getData(this.store);
    console.log('Subscribed value:', result);
  } catch (error) {
    console.error('Error:', error);
  }
}

-2
投票

这对我来说是工作。我会得到我的对象数据。

this.store.select('dataStore').subscribe(data => { 
          console.log(data)
 }
© www.soinside.com 2019 - 2024. All rights reserved.