Let's say a library developer Bob builds a module/library that exports a reducer and and action. He publishes the module to npm:
const INITIAL_STATE = { timesPoked : 0 };
export function makeSharedModuleAction(payload) {
return { 'type': 'POKE', payload };
}
export function sharedModuleReducer(state = INITIAL_STATE, action) {
if (action.type === 'POKE') {
if (action.payload === true) {
return { timesPoked: state.timesPoked + 1 };
} else {
return { timesPoked: state.timesPoked - 1 };
}
}
return state;
};
Now let's say an application developer Jenny builds and application and wants to use Bob's shared module. She builds a reducer for her application logic:
// application-logic.js
const INITIAL_STATE = { isPoked : false };
export function makeApplicationLogicAction(payload) {
return { 'type': 'POKE', payload };
}
export function applicationLogicReducer(state = INITIAL_STATE, action) {
if (action.type === 'POKE') return {
isPoked: action.payload
};
return state;
};
Then she builds her global application store and reducer, incorporating both the sharedModule
and applicationLogic
states through combineReducers
:
// store-reducer.js
import { combineReducers, createStore } from 'redux';
import {
sharedModuleReducer,
makeSharedModuleAction,
} from './shared-module';
import {
applicationLogicReducer,
makeApplicationLogicAction,
} from './application-logic';
const storeReducer = combineReducers({
sharedModuleState: sharedModuleReducer,
applicationLogicState: applicationLogicReducer
});
const store = createStore(storeReducer);
Jenny then decides to dispatch some actions to do the following:
- increment the poke count on sharedModuleState
- set applicationLogicState to poked
- decrement the poke count on sharedModuleState:
store.dispatch(makeSharedModuleAction(true));
store.dispatch(makeApplicationLogicAction(true));
store.dispatch(makeSharedModuleAction(false));
She's expecting the following state after this:
{
sharedModuleState: { timesPoked: 0 },
applicationLogicState: { isPoked: true }
}
She inspects the state and... oh no! The state is mangled!
const state = store.getState();
console.log(JSON.stringify(state, null, 2));
{
"sharedModuleState": {
"timesPoked": 1
},
"applicationLogicState": {
"isPoked": false
}
}
She poked the application logic state! Why is applicationLogicState set to false
?
She called poked and then un-poked the shared module state, so the timesPoked count should be 0! Why is it set to 1?
To figure this out, she would need to debug the application and trace the state transitions through all reducers. If the application contains dozens of reducers, it will be a long and annoying debugging adventure. If the imported reducers come from babelified modules without sourcemaps, it will be an extremely annoying debugging adventure.