Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Nested reducers aren't working anymore with V4 #306

Closed
Braincompiler opened this issue Aug 21, 2017 · 6 comments
Closed

Nested reducers aren't working anymore with V4 #306

Braincompiler opened this issue Aug 21, 2017 · 6 comments

Comments

@Braincompiler
Copy link

I'm submitting a...


[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

What is the current behavior?

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.

Expected behavior:

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.

Minimal reproduction of the problem with instructions:

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).

@brandonroberts
Copy link
Member

brandonroberts commented Aug 21, 2017

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 }
];

@Braincompiler
Copy link
Author

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!

@brandonroberts
Copy link
Member

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

@hagai26
Copy link

hagai26 commented Dec 17, 2017

There is a solution for nested reducers on V4 on this thread: ngrx/store#214 by @KwintenP

@mpaul31
Copy link

mpaul31 commented Jan 8, 2018

@brandonroberts anyway you could show code how it would look like with another level of reducer nesting like brians example?

@aaronfrost
Copy link

@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.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants