Split state and dispatch contexts

by Niels Krijger

HTML

<div id="app"></div>

CSS

body {
  background-color: #F2F5F6;
  font-family: sans-serif;
}

button {
  min-width: 3em;
  height: 2em;
  text-align: center;
}

h1 {
  font-size: 2em;
  font-weight: bold;
  padding-bottom: .3em;
}

h2 {
  font-size: 1.5em;
  font-weight: bold;
 
}

div {
  padding: .5em 1em;
}

React

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

const StateContext = React.createContext();
const DispatchContext = React.createContext();

const MyContextProvider = ({ children }) => {
  const [state, dispatch] =  React.useReducer(reducer, { count: 0 });

  return (
    <DispatchContext.Provider value={dispatch}>
      <StateContext.Provider value={state}>
        {children}
      </StateContext.Provider>
    </DispatchContext.Provider>
  );
};

let button1renders = 0;

const Button1 = () => {
  button1renders += 1;
  const dispatch = React.useContext(DispatchContext);
  
  return (
  	<div className="block">
      <button type="input" onClick={() => dispatch({ type: 'increment' }) }>+ 1</button>
      &nbsp; This button rendered {button1renders} times
    </div>
  );
}

let button2renders = 0;

const Button2 = () => {
  button2renders += 1;
  const dispatch = React.useContext(DispatchContext);
  
  return (
  	<div className="block">
      <button type="input" onClick={() => dispatch({ type: 'decrement' }) }>- 1</button>
      &nbsp; This button rendered {button2renders} times
    </div>
  );
}

const StateCount = () => {
  const state = React.useContext(StateContext);
  
  return (
  	<div className="block">
  	  <strong>Counter: {state.count}</strong>
  	</div>
  )
};

const App = props => {
	return (
	  <MyContextProvider>
      <h1>Split state and dispatch contexts</h1>
      <StateCount />
      <Button1 />
      <Button2 />
    </MyContextProvider>
  );
};

ReactDOM.render(<App />, document.getElementById('app'));