选择器不反映减速器中的状态
Selector doesn't reflect state in reducer
描述
在这个Minimal Reproductible Example我不明白为什么reducer有东西(我们可以在ReduxDevTools中观察它)但是选择器在组件中放置了undefined。
有人可以分享有关此行为的密钥吗?
代码
以下是 MRE 的部分内容:
- actions/index.ts
import { createAction } from '@ngrx/store';
export const µAppInitializerEntered = createAction(`[frontend] µAppInitializerEntered`);
- components/index.html
<pre>{{ feature$ | async | json }}</pre>
- components/index.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { $feature } from '../selectors';
import { tap } from 'rxjs/operators'
@Component({
selector: 'workspace-index',
templateUrl: './index.component.html',
styleUrls: ['./index.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class IndexComponent {
feature$ = this.store.pipe(select($feature), tap(feature => {
console.log({ feature })
}))
constructor(private store: Store<{}>) { }
}
- reducers/app-initializer/index.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { produce } from 'immer';
import { µAppInitializerEntered } from '../../actions';
export interface AppInitializer {
status: 'initial' | 'entered';
}
export const appInitializer = createReducer(
{
status: 'initial' as AppInitializer['status']
},
on(µAppInitializerEntered, (state): AppInitializer => produce(state, (draft) => {
draft.status = 'entered';
}))
);
- reducers/index.ts
import { InjectionToken } from '@angular/core';
import { Action, ActionReducerMap, MetaReducer } from '@ngrx/store';
import { appInitializer, AppInitializer } from './app-initializer/index.reducer';
export interface FeatureState {
appInitializer: AppInitializer;
}
export interface State {
frontend: FeatureState;
}
export const reducers = new InjectionToken<ActionReducerMap<FeatureState, Action>>('frontend', {
factory: () => ({ appInitializer })
});
- selectors/index.ts
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { FeatureState, State } from '../reducers';
export const $feature = createFeatureSelector<State, FeatureState>('frontend');
export const $appInitializer = createSelector($feature, (feature) => feature?.appInitializer);
export const $appInitializerEntered = createSelector($appInitializer, (appInitializer) => appInitializer?.status);
- index.module.ts
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Store, StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { µAppInitializerEntered } from './actions';
import { IndexComponent } from './components/index.component';
import { reducers } from './reducers';
@NgModule({
bootstrap: [IndexComponent],
declarations: [IndexComponent],
imports: [
BrowserModule,
StoreModule.forRoot(reducers, {
runtimeChecks: {
strictActionImmutability: true,
strictActionSerializability: true,
strictStateImmutability: true,
strictStateSerializability: true
}
}),
StoreDevtoolsModule.instrument({
maxAge: 5000,
name: 'frontend'
})
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: (store: Store<{}>) => () => store.dispatch(µAppInitializerEntered()),
multi: true,
deps: [Store]
}
]
})
export class AppModule {
}
上下文
- package.json#dependencies
"@angular/animations": "^10.1.0",
"@angular/common": "^10.1.0",
"@angular/compiler": "^10.1.0",
"@angular/core": "^10.1.0",
"@angular/forms": "^10.1.0",
"@angular/platform-browser": "^10.1.0",
"@angular/platform-browser-dynamic": "^10.1.0",
"@angular/platform-server": "^10.1.0",
"@angular/router": "^10.1.0",
"@ngrx/effects": "^10.0.1",
"@ngrx/router-store": "^10.0.1",
"@ngrx/store": "^10.0.1",
"@nrwl/node": "^10.4.4",
"angular-oauth2-oidc": "^10.0.3",
"immer": "^8.0.0",
"lodash.random": "^3.2.0",
"rxjs": "~6.5.5",
"tslib": "^2.0.0",
"zone.js": "^0.10.2"
- 纱线运行 nx报告
yarn run v1.21.1
$ nx report
> NX Report complete - copy this into the issue template
nx : Not Found
@nrwl/angular : 10.4.4
@nrwl/cli : 10.4.4
@nrwl/cypress : 10.4.4
@nrwl/eslint-plugin-nx : 10.4.4
@nrwl/express : Not Found
@nrwl/jest : 10.4.4
@nrwl/linter : 10.4.4
@nrwl/nest : Not Found
@nrwl/next : Not Found
@nrwl/node : 10.4.4
@nrwl/react : Not Found
@nrwl/schematics : Not Found
@nrwl/tao : 10.4.4
@nrwl/web : Not Found
@nrwl/workspace : 10.4.4
typescript : 4.0.5
Done in 1.68s.
备注
我最初将这个问题发布在 @ngrx/platform
repo (here) 而不是 Whosebug 的这里,因为我试图非常仔细地遵循 NgRx 文档来做这个 MRE 所以我想也许我在某处遗漏了一份文档,我们可以在官方文档中提供更多可见性。
正如 Tim Deschryver 所说 here,状态结构为:
{ appInitializer: { ... } }
您必须更改根减速器或 select 特征中的 appInitizalizer
叶子的工厂 select 或
export const reducers = new InjectionToken<ActionReducerMap<any, Action>>(
'frontend',
{
// use the `frontend` property here, otherwise it will be `appInitializer`
factory: () => ({ frontend: appInitializer }),
}
);
// or
export const $feature = createFeatureSelector<State, AppInitializer>('appInitializer ');
描述
在这个Minimal Reproductible Example我不明白为什么reducer有东西(我们可以在ReduxDevTools中观察它)但是选择器在组件中放置了undefined。
有人可以分享有关此行为的密钥吗?
代码
以下是 MRE 的部分内容:
- actions/index.ts
import { createAction } from '@ngrx/store';
export const µAppInitializerEntered = createAction(`[frontend] µAppInitializerEntered`);
- components/index.html
<pre>{{ feature$ | async | json }}</pre>
- components/index.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { $feature } from '../selectors';
import { tap } from 'rxjs/operators'
@Component({
selector: 'workspace-index',
templateUrl: './index.component.html',
styleUrls: ['./index.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class IndexComponent {
feature$ = this.store.pipe(select($feature), tap(feature => {
console.log({ feature })
}))
constructor(private store: Store<{}>) { }
}
- reducers/app-initializer/index.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { produce } from 'immer';
import { µAppInitializerEntered } from '../../actions';
export interface AppInitializer {
status: 'initial' | 'entered';
}
export const appInitializer = createReducer(
{
status: 'initial' as AppInitializer['status']
},
on(µAppInitializerEntered, (state): AppInitializer => produce(state, (draft) => {
draft.status = 'entered';
}))
);
- reducers/index.ts
import { InjectionToken } from '@angular/core';
import { Action, ActionReducerMap, MetaReducer } from '@ngrx/store';
import { appInitializer, AppInitializer } from './app-initializer/index.reducer';
export interface FeatureState {
appInitializer: AppInitializer;
}
export interface State {
frontend: FeatureState;
}
export const reducers = new InjectionToken<ActionReducerMap<FeatureState, Action>>('frontend', {
factory: () => ({ appInitializer })
});
- selectors/index.ts
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { FeatureState, State } from '../reducers';
export const $feature = createFeatureSelector<State, FeatureState>('frontend');
export const $appInitializer = createSelector($feature, (feature) => feature?.appInitializer);
export const $appInitializerEntered = createSelector($appInitializer, (appInitializer) => appInitializer?.status);
- index.module.ts
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Store, StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { µAppInitializerEntered } from './actions';
import { IndexComponent } from './components/index.component';
import { reducers } from './reducers';
@NgModule({
bootstrap: [IndexComponent],
declarations: [IndexComponent],
imports: [
BrowserModule,
StoreModule.forRoot(reducers, {
runtimeChecks: {
strictActionImmutability: true,
strictActionSerializability: true,
strictStateImmutability: true,
strictStateSerializability: true
}
}),
StoreDevtoolsModule.instrument({
maxAge: 5000,
name: 'frontend'
})
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: (store: Store<{}>) => () => store.dispatch(µAppInitializerEntered()),
multi: true,
deps: [Store]
}
]
})
export class AppModule {
}
上下文
- package.json#dependencies
"@angular/animations": "^10.1.0",
"@angular/common": "^10.1.0",
"@angular/compiler": "^10.1.0",
"@angular/core": "^10.1.0",
"@angular/forms": "^10.1.0",
"@angular/platform-browser": "^10.1.0",
"@angular/platform-browser-dynamic": "^10.1.0",
"@angular/platform-server": "^10.1.0",
"@angular/router": "^10.1.0",
"@ngrx/effects": "^10.0.1",
"@ngrx/router-store": "^10.0.1",
"@ngrx/store": "^10.0.1",
"@nrwl/node": "^10.4.4",
"angular-oauth2-oidc": "^10.0.3",
"immer": "^8.0.0",
"lodash.random": "^3.2.0",
"rxjs": "~6.5.5",
"tslib": "^2.0.0",
"zone.js": "^0.10.2"
- 纱线运行 nx报告
yarn run v1.21.1
$ nx report
> NX Report complete - copy this into the issue template
nx : Not Found
@nrwl/angular : 10.4.4
@nrwl/cli : 10.4.4
@nrwl/cypress : 10.4.4
@nrwl/eslint-plugin-nx : 10.4.4
@nrwl/express : Not Found
@nrwl/jest : 10.4.4
@nrwl/linter : 10.4.4
@nrwl/nest : Not Found
@nrwl/next : Not Found
@nrwl/node : 10.4.4
@nrwl/react : Not Found
@nrwl/schematics : Not Found
@nrwl/tao : 10.4.4
@nrwl/web : Not Found
@nrwl/workspace : 10.4.4
typescript : 4.0.5
Done in 1.68s.
备注
我最初将这个问题发布在 @ngrx/platform
repo (here) 而不是 Whosebug 的这里,因为我试图非常仔细地遵循 NgRx 文档来做这个 MRE 所以我想也许我在某处遗漏了一份文档,我们可以在官方文档中提供更多可见性。
正如 Tim Deschryver 所说 here,状态结构为:
{ appInitializer: { ... } }
您必须更改根减速器或 select 特征中的 appInitizalizer
叶子的工厂 select 或
export const reducers = new InjectionToken<ActionReducerMap<any, Action>>(
'frontend',
{
// use the `frontend` property here, otherwise it will be `appInitializer`
factory: () => ({ frontend: appInitializer }),
}
);
// or
export const $feature = createFeatureSelector<State, AppInitializer>('appInitializer ');