[x] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request
I have to migrate a huge codebase using ngrx@v2 to ngrx@v4 (Cause we need the better support for lazy-loaded modules). In v2 we were able to use combineReducers even with AOT but after migrating to v4 there will be either a compile error according to resolving symbol values statically or if I remove the combineReducers calls the reducers with nested reducers are missing on the state.
The compile error:
ERROR in Error encountered resolving symbol values statically. Calling function 'combineReducers', function calls are not supported. Consider replacing the function or lambda with a reference to an exported function, resolving symbol reducers in /Users/XXX/Projects/ngrx-bugs/ngrx-nested-reducers/src/app/reducers.ts, resolving symbol AppModule in /Users/XXX/Projects/ngrx-bugs/ngrx-nested-reducers/src/app/app.module.ts, resolving symbol AppModule in /Users/XXX/Projects/ngrx-bugs/ngrx-nested-reducers/src/app/app.module.ts, resolving symbol AppModule in /Users/XXX/Projects/ngrx-bugs/ngrx-nested-reducers/src/app/app.module.ts
If I use npm start (ng serve) the first round of compiling fails with the error above, if I change a file and trigger the watcher all the subsequent compilations will be successful and the reducers work as expected.
I want to use the current (nested) reducers (up to 5 levels deep in some cases). Of course I could flatten all the reducers but this would be a lot of refactoring I hope to avoid.
I created a small app which reproduces the behavior: https://github.com/Braincompiler/ngrx-nested-reducers (If you remove the combineReducers calls the state is empty).
You need to use a reducer token if you're calling a function like combineReducers to compose your reducers. https://github.com/ngrx/platform/blob/master/docs/store/api.md#injecting-reducers
app.module.ts
import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
import { StoreModule } from '@ngrx/store'
import { AppRoutingModule } from './app-routing.module'
import { AppComponent } from './app.component'
import { reducers, reducerToken, reducerProvider } from './reducers';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
StoreModule.forRoot(reducerToken),
],
providers: [reducerProvider],
bootstrap: [ AppComponent ]
})
export class AppModule { }
reducers.ts
import { ActionReducerMap, combineReducers } from '@ngrx/store'
import * as fromApp from './appReducers'
import * as fromNested from './nestedReducers'
import { InjectionToken } from '@angular/core';
export interface IState {
app: {
a: fromApp.IState,
b: fromNested.IState,
}
}
export const reducers = combineReducers({
a: fromApp.reducer,
b: fromNested.reducer,
});
export const reducerToken = new InjectionToken<ActionReducerMap<IState>>('Reducers');
export function getReducers() {
return {
app: reducers,
};
}
export const reducerProvider = [
{ provide: reducerToken, useFactory: getReducers }
];
Thanks a lot!
Ok, I supposed that but what if fromNested.reducer is a combination of yet another reducers? That's what I meant with "(up to 5 levels deep in some cases)".
Let's assume fromNested.reducer looks like that:
export const reducer = combineReducers({
reducer1,
reducer2,
yetNestedReducer: fromYetNested.reducer,
yetNestedReducer2: fromYetNested2.reducer,
// ...
})
How do I realize it now?
Thanks a lot in advance. I really appreciate your help!
You don't have to do anything different in nesting your reducers. The only change is how they get registered with the NgModule so it's AoT compatible
There is a solution for nested reducers on V4 on this thread: https://github.com/ngrx/store/pull/214 by @KwintenP
@brandonroberts anyway you could show code how it would look like with another level of reducer nesting like brians example?
@brandonroberts if I use your exact same pattern with the exception that I am doing
StoreModule.forFeature('foo', fooReducerToken)
instead of
StoreModule.forRoot(fooReducerToken)
Does the forFeature change anything? I am guessing you will say no. But I am getting some ridiculous errors after this, and I am not sure what is going on.
Most helpful comment
You need to use a reducer token if you're calling a function like
combineReducersto compose your reducers. https://github.com/ngrx/platform/blob/master/docs/store/api.md#injecting-reducersapp.module.ts
reducers.ts