如何用@ngrx/store获取State对象的当前值?
How to get current value of State object with @ngrx/store?
我的服务 class 在调用 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) 作为指导。
我想知道是否存在更好(更清洁)的方法?
@ngrx/store的原始答案v1.x
@ngrx/store
扩展了 BehaviorSubject 并且它有一个 value
属性 你可以使用。
this._store.value
这将是您应用程序的当前状态,您可以从那里 select 属性、过滤器、地图等...
更新:
我花了一些时间来弄清楚你的例子中的内容(:要获取 dataForUpdate
的当前值,你可以使用:
let x = this._store.value.StateReducer.dataForUpdate;
console.log(x); // => { key: "123" }
更新@ngrx/store v2.x
随着版本 2 的更新,value
已删除,如 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;
}
补充说明。当我使用 this._store.value.StateReducer.currentPeriod.id
转译器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 }; }) ;
}
为了解决问题,我更改了 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;
}
不确定这是否是合法的修改 ;)
withLatestFrom()
或 combineLatest()
订阅链中的方法可以满足您的需求,并且符合 Observables+Ngrx 的精神。
代替上面代码中的 GET STATE .mergeMap()
,使用 withLatestFrom()
看起来像这样:
...
.withLatestFrom(store$, (payload, state) => {
return {payload: payload, stateData: state.data}
} )
...
顺便说一句,原始问题中的代码似乎在管理 redux 操作的异步效果,这正是 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
严格来说,这不是问题的直接答案,但我发现此页面正在寻找如何从商店中检索单个值。
为此,您可以从 @ngrx/store
注入 State
对象,如下所示:
import { State } from '@ngrx/store';
constructor (private state: State<AppState>) {
let propertyValue = state.getValue().path.to.state.property;
}
state
对象将当前状态保存在私有 _value
属性 中,可通过 .getValue()
方法访问。
我创建了一个简约的应用程序,它的状态有 2 个计数器(AppState 的属性)和 2 个减速器。每个 reducer 都绑定到一个特定的计数器,我已经为每个计数器订阅了一个可观察到的值 console.log
。减速器本身也会在调用时写入控制台。
有一个按钮可以通过调度事件来调用两个 reducer。此外,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 { }
我的解决方案
ngStore中的Stateclass是一个BehaviorSubject,所以我们可以注入它,然后用它的值属性得到最新的值
constructor(private state:State<YourState>...) {
}
someMethod() {
// WHAT'S MORE: you can use your selector directly on it!
let v = yourSelector(this.state.value);
}
这对我有用。我会得到我的对象数据。
this.store.select('dataStore').subscribe(data => {
console.log(data)
}
根据@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;
}
如果你想让你的代码更具可读性,你也可以像这样使用 async/await 机制:
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;
}
这对我有用。您需要从“@ngrx/store”导入商店,AppState 是您的状态。
private state: AppState;
constructor(private store: Store<AppState>) { }
ngOnInit() {
this.store.select(x => this.state = x).subscribe();
}
更新@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;
}
这只是我对这个问题的经验,而不是标准 code
。
请参阅github中的答案:
State snapshot #227
我想在 constractor
中得到 state
所以我使用 unasynchronous
:
constructor (private state: State<AppState>) {
this.store.select("yourSelector").forEach(yourSelector => {
this.property = yourSelector.path.to.state.property
});
}
我的服务 class 在调用 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) 作为指导。
我想知道是否存在更好(更清洁)的方法?
@ngrx/store的原始答案v1.x
@ngrx/store
扩展了 BehaviorSubject 并且它有一个 value
属性 你可以使用。
this._store.value
这将是您应用程序的当前状态,您可以从那里 select 属性、过滤器、地图等...
更新:
我花了一些时间来弄清楚你的例子中的内容(:要获取 dataForUpdate
的当前值,你可以使用:
let x = this._store.value.StateReducer.dataForUpdate;
console.log(x); // => { key: "123" }
更新@ngrx/store v2.x
随着版本 2 的更新,value
已删除,如 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;
}
补充说明。当我使用 this._store.value.StateReducer.currentPeriod.id
转译器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 }; }) ;
}
为了解决问题,我更改了 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;
}
不确定这是否是合法的修改 ;)
withLatestFrom()
或 combineLatest()
订阅链中的方法可以满足您的需求,并且符合 Observables+Ngrx 的精神。
代替上面代码中的 GET STATE .mergeMap()
,使用 withLatestFrom()
看起来像这样:
...
.withLatestFrom(store$, (payload, state) => {
return {payload: payload, stateData: state.data}
} )
...
顺便说一句,原始问题中的代码似乎在管理 redux 操作的异步效果,这正是 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
严格来说,这不是问题的直接答案,但我发现此页面正在寻找如何从商店中检索单个值。
为此,您可以从 @ngrx/store
注入 State
对象,如下所示:
import { State } from '@ngrx/store';
constructor (private state: State<AppState>) {
let propertyValue = state.getValue().path.to.state.property;
}
state
对象将当前状态保存在私有 _value
属性 中,可通过 .getValue()
方法访问。
我创建了一个简约的应用程序,它的状态有 2 个计数器(AppState 的属性)和 2 个减速器。每个 reducer 都绑定到一个特定的计数器,我已经为每个计数器订阅了一个可观察到的值 console.log
。减速器本身也会在调用时写入控制台。
有一个按钮可以通过调度事件来调用两个 reducer。此外,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 { }
我的解决方案
ngStore中的Stateclass是一个BehaviorSubject,所以我们可以注入它,然后用它的值属性得到最新的值
constructor(private state:State<YourState>...) {
}
someMethod() {
// WHAT'S MORE: you can use your selector directly on it!
let v = yourSelector(this.state.value);
}
这对我有用。我会得到我的对象数据。
this.store.select('dataStore').subscribe(data => { console.log(data) }
根据@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;
}
如果你想让你的代码更具可读性,你也可以像这样使用 async/await 机制:
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;
}
这对我有用。您需要从“@ngrx/store”导入商店,AppState 是您的状态。
private state: AppState;
constructor(private store: Store<AppState>) { }
ngOnInit() {
this.store.select(x => this.state = x).subscribe();
}
更新@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;
}
这只是我对这个问题的经验,而不是标准 code
。
请参阅github中的答案: State snapshot #227
我想在 constractor
中得到 state
所以我使用 unasynchronous
:
constructor (private state: State<AppState>) {
this.store.select("yourSelector").forEach(yourSelector => {
this.property = yourSelector.path.to.state.property
});
}