Ngrx/data 似乎干扰了减速器
Ngrx/data appears to interfere with reducers
我对 Ngrx 还很陌生,所以我假设我做错了什么。看来,如果 Ngrx/data 处于活动状态,那么它会干扰状态历史,导致 reducer 被调用时具有未定义的初始状态。
如果我在根中有多个状态,这是显而易见的。一个状态的减速器会影响另一个状态。
我创建了一个小应用程序来演示这个问题。我将 EntityDataModule 添加到我的模块文件中,如下所示:
imports: [
BrowserModule,
HttpClientModule,
EntityDataModule.forRoot({ entityMetadata: { Hero: {} }, pluralNames: { Hero: 'Heroes' }}),
BrowserAnimationsModule,
StoreModule.forRoot(reducers),
EffectsModule.forRoot([ EffectService ]),
StoreDevtoolsModule.instrument(),
],
这是我的商店代码:
import { ActionReducerMap, createAction, createReducer, createSelector, on, props } from '@ngrx/store';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';
export interface FirstState {
value: string;
}
export interface SecondState {
value: string;
}
export interface State {
first: FirstState;
second: SecondState;
}
const initialFirstState: FirstState = {
value: 'firstValue'
};
const initialSecondState: SecondState = {
value: 'secondValue'
};
export const selectFirstState = (state: State) => state.first;
export const selectSecondState = (state: State) => state.second;
export const selectFirstValue = createSelector(selectFirstState, (state: FirstState) => state.value);
export const selectSecondValue = createSelector(selectSecondState, (state: SecondState) => state.value);
export const indirectAction = createAction(
'[Indirect] Use Effects',
);
export const firstValueSet = createAction(
'[First] Set Value',
props<{ theValue: string }>()
);
export const secondValueSet = createAction(
'[Second] Set Value',
props<{ theValue: string }>()
);
export const firstReducer = createReducer(
initialFirstState,
on(firstValueSet, (state, { theValue }) => ({ ...state, value: theValue }))
);
export const secondReducer = createReducer(
initialSecondState,
on(secondValueSet, (state, { theValue }) => ({ ...state, value: theValue }))
);
export const reducers: ActionReducerMap<State> = {
first: firstReducer,
second: secondReducer
};
@Injectable()
export class EffectService {
constructor(private actions$: Actions) {}
indirect$ = createEffect(() => this.actions$.pipe(
ofType(indirectAction),
map(action => secondValueSet({ theValue: 'INDIRECT' }))
));
}
我在这里创建了一个 Stackblitz 应用程序:https://stackblitz.com/edit/angular-bttdek
如果您单击任何按钮,它只会调度操作:
setFirstValue() {
this.store.dispatch(firstValueSet({ theValue: 'S1:' + Math.random() }));
}
setSecondValue() {
this.store.dispatch(secondValueSet({ theValue: 'S2:' + Math.random() }));
}
indirect() {
this.store.dispatch(indirectAction());
}
你会看到另一个状态重置为初始状态。如果我从模块中删除 EntityDataModule.forRoot() ,它会正常工作。因此,似乎只是让 EntityDataModule 处于活动状态会导致商店出现问题。我浏览了商店,发现最新的未提交状态的索引是一个差一个,导致使用未定义的初始状态调用 reducer。
With EntityDataModule active
如果我像这样删除 EntityDataModule:
imports: [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
StoreModule.forRoot(reducers),
EffectsModule.forRoot([ EffectService ]),
StoreDevtoolsModule.instrument(),
],
有效:
With EntityDataModule disabled
我是不是配置有误?
必须首先加载商店模块,这应该可以解决您的问题:
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
// ---
StoreModule.forRoot(reducers),
EntityDataModule.forRoot({ entityMetadata: { Hero: {} }, pluralNames: { Hero: 'Heroes' }}),
// ---
EffectsModule.forRoot([ EffectService ]),
StoreDevtoolsModule.instrument(),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
我对 Ngrx 还很陌生,所以我假设我做错了什么。看来,如果 Ngrx/data 处于活动状态,那么它会干扰状态历史,导致 reducer 被调用时具有未定义的初始状态。
如果我在根中有多个状态,这是显而易见的。一个状态的减速器会影响另一个状态。
我创建了一个小应用程序来演示这个问题。我将 EntityDataModule 添加到我的模块文件中,如下所示:
imports: [
BrowserModule,
HttpClientModule,
EntityDataModule.forRoot({ entityMetadata: { Hero: {} }, pluralNames: { Hero: 'Heroes' }}),
BrowserAnimationsModule,
StoreModule.forRoot(reducers),
EffectsModule.forRoot([ EffectService ]),
StoreDevtoolsModule.instrument(),
],
这是我的商店代码:
import { ActionReducerMap, createAction, createReducer, createSelector, on, props } from '@ngrx/store';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';
export interface FirstState {
value: string;
}
export interface SecondState {
value: string;
}
export interface State {
first: FirstState;
second: SecondState;
}
const initialFirstState: FirstState = {
value: 'firstValue'
};
const initialSecondState: SecondState = {
value: 'secondValue'
};
export const selectFirstState = (state: State) => state.first;
export const selectSecondState = (state: State) => state.second;
export const selectFirstValue = createSelector(selectFirstState, (state: FirstState) => state.value);
export const selectSecondValue = createSelector(selectSecondState, (state: SecondState) => state.value);
export const indirectAction = createAction(
'[Indirect] Use Effects',
);
export const firstValueSet = createAction(
'[First] Set Value',
props<{ theValue: string }>()
);
export const secondValueSet = createAction(
'[Second] Set Value',
props<{ theValue: string }>()
);
export const firstReducer = createReducer(
initialFirstState,
on(firstValueSet, (state, { theValue }) => ({ ...state, value: theValue }))
);
export const secondReducer = createReducer(
initialSecondState,
on(secondValueSet, (state, { theValue }) => ({ ...state, value: theValue }))
);
export const reducers: ActionReducerMap<State> = {
first: firstReducer,
second: secondReducer
};
@Injectable()
export class EffectService {
constructor(private actions$: Actions) {}
indirect$ = createEffect(() => this.actions$.pipe(
ofType(indirectAction),
map(action => secondValueSet({ theValue: 'INDIRECT' }))
));
}
我在这里创建了一个 Stackblitz 应用程序:https://stackblitz.com/edit/angular-bttdek
如果您单击任何按钮,它只会调度操作:
setFirstValue() {
this.store.dispatch(firstValueSet({ theValue: 'S1:' + Math.random() }));
}
setSecondValue() {
this.store.dispatch(secondValueSet({ theValue: 'S2:' + Math.random() }));
}
indirect() {
this.store.dispatch(indirectAction());
}
你会看到另一个状态重置为初始状态。如果我从模块中删除 EntityDataModule.forRoot() ,它会正常工作。因此,似乎只是让 EntityDataModule 处于活动状态会导致商店出现问题。我浏览了商店,发现最新的未提交状态的索引是一个差一个,导致使用未定义的初始状态调用 reducer。
With EntityDataModule active
如果我像这样删除 EntityDataModule:
imports: [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
StoreModule.forRoot(reducers),
EffectsModule.forRoot([ EffectService ]),
StoreDevtoolsModule.instrument(),
],
有效: With EntityDataModule disabled
我是不是配置有误?
必须首先加载商店模块,这应该可以解决您的问题:
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
// ---
StoreModule.forRoot(reducers),
EntityDataModule.forRoot({ entityMetadata: { Hero: {} }, pluralNames: { Hero: 'Heroes' }}),
// ---
EffectsModule.forRoot([ EffectService ]),
StoreDevtoolsModule.instrument(),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }