Memoize context values

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 MyContext = React.createContext();

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

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

const ButtonParent = () => {
  const { dispatch } = React.useContext(MyContext);
 
  const handleButtonClick = React.useCallback(() => {
    dispatch({ type: 'increment' });
  }, [dispatch]);
  
  return (
    <ButtonIncrease onClick={handleButtonClick} />
  );
}

let buttonIncreaseRenders = 0;

const ButtonIncrease = React.memo(({ onClick }) => {
  buttonIncreaseRenders += 1;
  
  return (
    <div className="block">
      <button type="input" onClick={onClick}>+ 1</button>
      &nbsp; This button rendered {buttonIncreaseRenders} times
    </div>
  );
});

let buttonDecreaseRenders = 0;

const ButtonDecrease = () => {
  const { dispatch } = React.useContext(MyContext);
  
  const handleButtonClick = React.useCallback(() => {
  	dispatch({ type: 'decrement' });
  }, [dispatch]);
  
  return React.useMemo(() => {
  	buttonDecreaseRenders += 1;
    return (
  	  <div className="block">
        <button type="input" onClick={handleButtonClick}>- 1</button>
        &nbsp; This button rendered {buttonDecreaseRenders} times
      </div>
    );
  }, [handleButtonClick]);
}

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

const App = props => {
	return (
	  <MyContextProvider>
      <h1>Memoize context values</h1>
      <StateCount />
      <ButtonParent />
      <ButtonDecrease />
    </MyContextProvider>
  );
};

ReactDOM.render(<App />,...