Using Redux how do you stop the switch in the reducer growing?
I'm using angular 5 and ng2-Redux.
I'm using the reducer and a switch but I'm worried that the switch will grow as I add more action types.
Currently I'm trying to use a red combine reducer to manage the growth of the state and the files:
I currently get this error:
Unhandled Promise rejection: Reducer "app" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined. If you don't want to set a value for this reducer, you can use null instead of undefined. ; Zone: ; Task: Promise.then ; Value: Error: Reducer "app" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined. If you don't want to set a value for this reducer, you can use null instead of undefined.
Seems like the rootReducer needs to return state but I'm using a combine reducer.
My Reducer Service code:
import { combineReducers } from 'redux';
export interface IAppState {
counter?: number;
user?: Object;
numberOfMessages?: number;
}
export interface IAction {
type: string;
payload?: any;
}
export const INITIAL_APP_STATE: IAppState = {
counter: 0,
user: {},
numberOfMessages: 0
};
export const appReducer = (state: IAppState, action: IAction): IAppState => {
switch (action.type) {
case 'INCREMENT':
return {
counter: state.counter + action.payload,
numberOfMessages: state.numberOfMessages
};
}
return state;
};
export const userReducer = (state: IAppState, action: IAction): IAppState => {
switch (action.type) {
case 'DECREMENT':
return {
counter: state.counter,
numberOfMessages: state.numberOfMessages - action.payload
};
}
return state;
};
export const rootReducer = combineReducers({
app: appReducer,
user: userReducer
});
This is my module export section for the redux setup (ERROR HERE I THINK????):
export class AppModule {
constructor(ngRedux: NgRedux<IAppState>) {
ngRedux.configureStore(rootReducer, INITIAL_APP_STATE);
}
}
This is my dispatch method:
public increment(): void {
this.ngRedux.dispatch({
type: '[app] INCREMENT',
payload: 1
});
}
public decrement(): void {
this.ngRedux.dispatch({
type: '[user] DECREMENT',
payload: 2
});
}
You should split up your IAppState
into different feature states:
export interface IAppState = {
auth: IAuthState;
onboarding: IOnboardingState;
feedback: IFeedbackState;
};
Then you build up your rootReducer
from all the feature reducers using combineReducers
from 'redux'
:
export const rootReducer = combineReducers({
auth: authReducer,
onboarding: onboardingReducer,
feedback: feedbackReducer
});
Your feature states, actions, reducers and epics will therefore be smaller and easier to maintain.
You can add feature specific prefixes to your action names to ensure they are unique:
export class CounterActions {
static INCREMENT: string = '[COUNTER] INCREMENT';
static DECREMENT: string = '[COUNTER] DECREMENT';
static RANDOMIZE: string = '[COUNTER] RANDOMIZE';
The solution is in the error message, read carefully:
If the state passed to the reducer is undefined, you must explicitly return the initial state.
You need to pass in the initial state as a default value:
export const userReducer = (state: IUserState = INITIAL_USER_STATE, action: IAction): IAppState => {
^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
switch (action.type) {
case 'LOGIN':
return {
userName: action.payload.userName
};
}
return state;
};
Also, the userReducer
only gets the IUserState
slice of the state passed in.