@ngrx/store combine multiple reducers from feature module

Your setup is almost correct.

In the createFeatureSelector function you declare a feature key in the root of the store, 'exercises' in the example. So, you are going to select store.exercises.exercises.id for example and feature selector is just a shortcut to store.exercises.

However, in the StoreModule.forFeature('training', trainingReducers) call you defined 'training' as the root key for your feature module.

The correct setup could look like this:

export const featureReducersMap = {
  exercise: exerciseReducer,
  anyOtherKey: anyOtherReducer
};
StoreModule.forFeature('myFeatureModule', featureReducersMap);
export interface FeatureState{
  exercise: string;
  anyOtherKey: number;
}

Then write selectors like:

export const featureSelector = createFeatureSelector<FeatureState>('myFeatureModule');

export const exerciseSelector = createSelector(
  featureSelector,
  (state: FeatureState) => state.exercise
);

It is recommended to store the feature key in the variable instead of hardcoding it.


With new feature createFeature you can do it easier.

  1. Add feature to AppState interface
export interface AppState {
  yourFeature: FeatureInterface
}
  1. Create some feature inside module. For example
export interface State {
  key: string;
}

export const initialState: State = {
  key: ''
};

export const feature = createFeature({
  name: 'module',
  reducer: createReducer(
    initialState
  )
});

export const {
  name,
  reducer
} = feature;
  1. Combine all your reducers from module
import * as fromGreatFeature from './store/great-feature/great-feature.reducer';
import * as fromEntity from './store/entity/entity.reducer';
import * as fromModule from './store/module/module.reducer';


export interface ScheduleState {
  module: fromModule.State; // <- from example
  entity: fromEntity.State;
  greatFeature: fromGreatFeature.State;
}


export const reducers = combineReducers({
  module: fromModule.reducer, // <- from example 
  entity: fromEntity.reducer,
  greatFeature: fromGreatFeature.reducer
});
  1. Make separate file and define store module or make it inside main module file
export const yourFeature = createFeature<AppState>({
  name: 'yourFeature', // name from interface
  reducer: reducers // combined reducers
});

export const {
  name,
  reducer
} = yourFeature;-----------------
                                |
                                |
@NgModule({                     |
  imports: [                    |
    StoreModule.forFeature(yourFeature),
    EffectsModule.forFeature([Effects, Effects, Effects])
  ],
  exports: [StoreModule, EffectsModule]
})
export class LegendaryStoreModule {}
@NgModule({
  declarations: [
    SomeComponent
  ],
  imports: [
    SharedModule,
    LegendaryRoutingModule,
    LegendaryStoreModule, // <- your store module
  ],
  exports: [

  ]
})
export class YourLegendaryModule {}
  1. And selectors. Function createFeature automatically generated to you selectors
export const getSomeThing = createSelector(
  yourFeature.selectYourFeatureModuleState, // <- main feature state
  fromModule.selectSome // <- nested state
);

I can give you an example how I did it. I used an index.ts to bundle all other reducers from within the module like this:

module/reducers/index.ts

import * as fromRoot from '../../../reducers';
import * as fromSearch from './search';
import * as fromUserDetail from './user-detail';
import * as fromDetailBase from './base';


export interface UserModuleState {
  search: fromSearch.State;  
  detail: fromUserDetail.State;
  detailBase: fromDetailBase.State;
}

export interface State extends fromRoot.State {
    userModule: UserModuleState;    
}

export const reducers = {    
    search: fromSearch.reducer,
    detail: fromUserDetail.reducer,
    detailBase : fromDetailBase.reducer
};

export const selectUserModuleState = createFeatureSelector<UserModuleState>('userModule');


export const selectSearchState = createSelector(
    selectUserModuleState, (state: UserModuleState) => state.search
);
export const getSearchLoading = createSelector(selectSearchState, fromSearch.getLoading);
export const getSearchEntities = createSelector(selectSearchState, fromSearch.getEntities);

module/user.module.ts

import { reducers } from './reducers';
@NgModule({
    imports: [
        ...
        StoreModule.forFeature('userModule', reducers)
    ],
     ...
})
export default class UserModule { }