<html>
  <body>
    <div id="root"></div>
  </body>
</html>
.app {
  height: 200px;
  display: flex;
  justify-content: space-around;
  align-items: center;
  flex-direction: column;
}

p {
  margin: 0;
  font-weight: bold;
  font-size: 20px;
}
View Compiled
const { Component } = React;

class App extends Component {
  render() {
    return (
      <StateManager initialState={{ counter: 5 }}>
        <div className="app">
          <p>Counter Demo</p>
          <ConnectedCounterDisplay />
          <ConnectedCounterButton />
        </div>
      </StateManager>
    );
  }
}

const { Provider, Consumer } = React.createContext();

class StateManager extends Component {
  static Consumer = Consumer;

  state = { 
    counter: 0,
    ...this.props.initialState
  };

  increment = () => {
    this.setState(({ counter }) => ({ counter: counter + 1 }));
  };

  render() {
    return (
      <Provider
        value={{
          ...this.state,
          increment: this.increment
        }}
      >
        {this.props.children}
      </Provider>
    );
  }
}

function withState(stateMapperFn) {
  return (InnerComponent) => {
    const ConnectedComponent = () => (
      <StateManager.Consumer>
        {(state) => (
          <InnerComponent
            {...stateMapperFn(state)}
            {...this.props} // this allows props passed into the Component
                            // to override any state values
          />
        )}
      </StateManager.Consumer>
    );

    // This just adds syntactical sugar for the React debugger.
    ConnectedComponent.displayName = `withState(${InnerComponent.displayName || InnerComponent.name})`;

    return ConnectedComponent;
  };
}

const CounterDisplay = ({ counter }) => (
  <div>{counter}</div>
);

const ConnectedCounterDisplay = withState(({ counter }) => ({
  counter 
}))(CounterDisplay);

const CounterButton = ({ onClick }) => (
  <button onClick={onClick}>Increment</button>
);

const ConnectedCounterButton = withState(({ increment }) => ({
  onClick: () => increment() 
}))(CounterButton);

ReactDOM.render(
  (<App />),
  document.getElementById('root')
);
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/react@16/umd/react.development.js
  2. https://unpkg.com/react-dom@16/umd/react-dom.development.js