选择器不反映减速器中的状态

Selector doesn't reflect state in reducer

描述

在这个Minimal Reproductible Example我不明白为什么reducer有东西(我们可以在ReduxDevTools中观察它)但是选择器在组件中放置了undefined。

有人可以分享有关此行为的密钥吗?

代码

以下是 MRE 的部分内容:

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

export const µAppInitializerEntered = createAction(`[frontend] µAppInitializerEntered`);
<pre>{{ feature$ | async | json }}</pre>
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<{}>) { }
}
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';
    }))
);
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 })
});
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);
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 {
}

上下文

    "@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"
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 ');