Skip to content

Instantly share code, notes, and snippets.

@lxe
Last active January 16, 2019 00:25
Show Gist options
  • Save lxe/2724436003d495714617ea601000b609 to your computer and use it in GitHub Desktop.
Save lxe/2724436003d495714617ea601000b609 to your computer and use it in GitHub Desktop.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment