Build Yourself a Redux (thunk middleware)

This sample code goes along with a Zapier engineering blog post that walks you through building a mini version of redux and react-redux.

by justindeal

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.js"></script>
<script src="https://npmcdn.com/[email protected]/dist/react-with-addons.js"></script>
<script src="https://npmcdn.com/[email protected]/dist/react-dom.js"></script>
<div id="root"></div>

CSS

body {
  font-family: sans-serif;
  font-size: 14px;
}

.editor-button {
  border: gray solid 1px;
  background-color: white;
  min-width: 100px;
  font-size: 14px;
  border-radius: 3px;
}

.note-list {
  list-style: none;
  padding: 0px;
}

.note-list-item {
  font-size: 14px;
  margin-bottom: 5px;
}

.editor-content {
  font-size: 14px;
  margin: 10px 0px 14px;
  vertical-align: bottom;
  width: 90vw;
  height: 50vh;
}

Babel + JSX

///////////////////////////////
// Mini Redux implementation //
///////////////////////////////

const validateAction = action => {
  if (!action || typeof action !== 'object' || Array.isArray(action)) {
    throw new Error('Action must be an object!');
  }
  if (typeof action.type === 'undefined') {
    throw new Error('Action must have a type!');
  }
};

const createStore = (reducer, middleware) => {
  let state;
  const subscribers = [];
  const coreDispatch = action => {
    validateAction(action);
    state = reducer(state, action);
    subscribers.forEach(handler => handler());
  };
  const getState = () => state;
  const store = {
    dispatch: coreDispatch,
    getState,
    subscribe: handler => {
      subscribers.push(handler);
      return () => {
        const index = subscribers.indexOf(handler);
        if (index > 0) {
          subscribers.splice(index, 1);
        }
      };
    }
  };
  if (middleware) {
    const dispatch = action => store.dispatch(action);
    store.dispatch = middleware({
      dispatch,
      getState
    })(coreDispatch);
  }
  coreDispatch({type: '@@redux/INIT'});
  return store;
};

const applyMiddleware = (...middlewares) => store => {
  if (middlewares.length === 0) {
    return dispatch => dispatch;
  }
  if (middlewares.length === 1) {
    return middlewares[0](store);
  }
  const boundMiddlewares = middlewares.map(middleware =>
    middleware(store)
  );
  return boundMiddlewares.reduce((a, b) =>
    next => a(b(next))
  );
};

const thunkMiddleware = ({dispatch, getState}) => next => action => {
  if (typeof action === 'function') {
    return action(dispatch, getState);
  }
  return next(action);
};

const loggingMiddleware = ({getState}) => next => action => {
  console.info('before', getState());
  console.info('action', action);
  const result = next(action);
  console.info('after', getState());
  return result;
};

/////////////////////////////////////
// Mini React Redux implementation...