Reputation: 2723
In this Minimal Reproductible Example I don't understand why the reducer has something (we can observe it in the ReduxDevTools) but the selector puts undefined in the component.
Could someone have keys to share about this behavior?
Here are the parts of the 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.
I originally posted this issue on @ngrx/platform
repo (here) rather than in here in StackOverflow because I tried to follow the NgRx documentation very carefully to do this MRE so I thought that maybe I missed a documentation somewhere which we can bring more visibility on in the official documentation.
Upvotes: 2
Views: 537
Reputation: 108
As Tim Deschryver said here, the state is structured as:
{ appInitializer: { ... } }
You have to change the factory for the root reducer or select the appInitizalizer
leaf in the feature selector
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 ');
Upvotes: 2