Search code examples
angularentityngrxreduce

How to combine 3 reducers into one mainReducer and register it in Module using StoreModule.forFeature()


I have 3 objects implementing 3 different interfaces that I'm managing theirs states with ngrx (latest versions). The reducers are defined as: loginReducer, PrsnlReducer and userReducer. I'm handling the PrsnlReducer and the userReducer as ngrx entities because I want to take advantage of the ngrx entity build-in actions, and I handle the loginReducer with basic reducer concept (Not as an entity). These are the three:

loginReducer.ts

const initialState: AuthStateInterface = {
    isSubmitting: false,
    user_code: '',
    token: '',
    userId : '',
}
const logReducer = createReducer(
    initialState, 
    on(loginAction, 
    (state: AuthStateInterface): AuthStateInterface => ({
    ...state,
    isSubmitting: true
}))))

export function loginReducers( state: AuthStateInterface, action: Action){
    return logReducer(state, action)
}

userReducer.ts

export interface State extends EntityState<userStateInterface> {
} 
export const adapter: EntityAdapter<userStateInterface> = createEntityAdapter<userStateInterface>();
export const initialState: State = adapter.getInitialState({
}); 
const usrReducer = createReducer(
  initialState,
  on(UserActions.addUser, (state, { user }) => {
    return adapter.addOne(user, state)
  }),
  on(UserActions.setUser, (state, { user }) => {
    return adapter.setOne(user, state)
  })
);
 
export function userReducer(state: State | undefined, action: Action) {
  return usrReducer(state, action);
}

PrsnlReducer.ts

export interface State extends EntityState<prsnlStateInterface > {
  // additional entities state properties
  selectedUserId: number | null;
}
 
export const adapter: EntityAdapter<prsnlStateInterface > = createEntityAdapter<prsnlStateInterface >();
 
export const initialState: State = adapter.getInitialState({
  // additional entity state properties
  selectedUserId: null,
});
 
const persnlReducer = createReducer(
  initialState,
  on(PrsnlActions.addPrsnl, (state, { prsnl }) => {
    return adapter.addOne(prsnl, state)
  })
);

export function prsnlReducer(state: State | undefined, action: Action) {
  return persnlReducer(state, action);
}

Now I want to combine the 3 reducers into a single mainReducer in order to register it with the StoreModule.forFeature() inside my auth module. Because the ngrx combineReducer documentation is too succint to help, all I could do in the mainReducer.ts is as bellow:

mainReducer.ts

import * as fromLogin from 'src/app/auth/store/reducers/loginReducers';
import * as fromPrsnl from 'src/app/auth/store/reducers/prsnlReducer';
import * as fromUser from 'src/app/auth/store/reducers/userReducer';
export interface AuthModuleState {
    login: AuthStateInterface
    prsnl: prsnlStateInterface
    user: userStateInterface
}

export interface State {
    authModule: AuthModuleState;    
}

export const mainReducer = {    
    login: fromLogin,
    prsnl: fromPrsnl,
    user: fromUser
};

export const selectAuthModuleState = createFeatureSelector<AuthModuleState>('authModule');


export const selectLoginState = createSelector(
    selectAuthModuleState, (state: AuthModuleState) => state.login
);

Then I register the mainReducer.ts in the authModule with

StoreModule.forFeature('auth', mainReducer),

But visual studio errors out "that No overload matches this call" as you can see on screenshot: enter image description here

How can I then combine the 3 reducers properly and register them in module for use ? Or is there any better way to handle such goal ? I will really appreciate your help to solve this empedement. Thanks in advance.


Solution

  • If you want to centralize your reducers into one source of truth then use combineReducers() available in angular. You can adapt it to your case like this

    export interface AuthModuleState {
        login: any
        prsnl: any
        user: any
    }
    
    export const mainReducer:AuthModuleState = {    
        login: fromLogin.loginReducer,
        prsnl: fromPrsnl.prsnlReducer,
        user: fromUser.userReducer
    };
    
    const combineReducer = combineReducers(mainReducer)
    
    
    export function reducer(state:any, action:any){
      return combineReducer(state, action);
    }
    

    Then you just use the exported reducer in module as you already did with forFeature.

    StoreModule.forFeature('auth', reducer),