Build Yourself a Redux (provider and connect)

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 => {
  let state;
  const subscribers = [];
  const store = {
    dispatch: action => {
      validateAction(action);
      state = reducer(state, action);
      subscribers.forEach(handler => handler());
    },
    getState: () => state,
    subscribe: handler => {
      subscribers.push(handler);
      return () => {
        const index = subscribers.indexOf(handler);
        if (index > 0) {
          subscribers.splice(index, 1);
        }
      };
    }
  };
  store.dispatch({type: '@@redux/INIT'});
  return store;
};

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

const {PropTypes} = React;

class Provider extends React.Component {
  getChildContext() {
    return {
      store: this.props.store
    };
  }
  render() {
    return this.props.children;
  }
}

Provider.childContextTypes = {
  store: PropTypes.object
};

const connect = (
  mapStateToProps = () => ({}),
  mapDispatchToProps = () => ({})
) => Component => {
  class Connected extends React.Component {
    onStoreOrPropsChange(props) {
      const {store} = this.context;
      const state = store.getState();
      const stateProps = mapStateToProps(state, props);
      const dispatchProps = mapDispatchToProps(store.dispatch, props);
      this.setState({
        ...stateProps,
        ...dispatchProps
      });
    }
    componentWillMount() {
      const {store} = this.context;
      this.onStoreOrPropsChange(this.props);
      this.unsubscribe = store.subscribe(() =>
        this.onStoreOrPropsChange(this.props)
      );
    }
   ...