How to solve problem with too many re-renders in context?

Solution 1:

There is no render bailout for context consumers (v17).

Here is a demonstration, where the Consumer will always re-render just because he is a Context Consumer, even though he doesn't consume anything.

import React, { useState, useContext, useMemo } from "react";
import ReactDOM from "react-dom";

// People wonder why the component gets render although the used value didn't change
const Context = React.createContext();

const Provider = ({ children }) => {
  const [counter, setCounter] = useState(0);
  const value = useMemo(() => {
    const count = () => setCounter(p => p + 1);
    return [counter, count];
  }, [counter]);
  return <Context.Provider value={value}>{children}</Context.Provider>;
};

const Consumer = React.memo(() => {
  useContext(Context);
  console.log("rendered");
  return <>Consumer</>;
});

const ContextChanger = () => {
  const [, count] = useContext(Context);
  return <button onClick={count}>Count</button>;
};

const App = () => {
  return (
    <Provider>
      <Consumer />
      <ContextChanger />
    </Provider>
  );
};

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

Edit Context API Problem

To fix it:

  • Use a single context for each consumed value. Meaning that context holds a single value, (and no, there is no problem with multiple contexts in an application).
  • Use a state management solution like Recoil.js, Redux, MobX, etc. (although it might be overkill, think good about app design beforehand).
  • Minor optimization can be achieved by memoizing Provider's values with useMemo.