Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why react context provider component renders twice [duplicate]

I was playing around with React Context API and found that the provider renders twice at each value change.

Here is what I've done.

  1. Created a context, then a component which renders its provider.
  2. The provider provides a state value and its setter.
  3. Created a direct child that renders directly its children.
  4. Created a consumer which reads the context value and adds a button to change it.
  5. Each component executes a console.log when it renders.
  6. Added an effect to the provider without deps to log every time it renders.
  7. Added a ref in the provider that is incremented at each render and it is logged in both the render and the effect.

The problem ?

Each time I hit the button to change the context value, the provider renders twice, but the effect is executed only once.

So the ref is incremented always twice, but the effect only gets the last value each time (it skips a value!).

Also, at the first render of the provider it logs twice the same ref value, which is very strange to me.

Can anybody tell me why I am getting this behavior ?

Here is the code:

The provider:

const MyContext = React.createContext(0);

function Provider({ children }) {
  const state = React.useState("Yes");
  const ref = React.useRef(0);
  ref.current += 1;
  console.log("Context provider ", ref.current);
  React.useEffect(() => {
    console.log("effect on provider, ref value = ", ref.current);
  });

  return <MyContext.Provider value={state}>{children}</MyContext.Provider>;
}

The two children

function DirectChild({ children }) {
  console.log("provider direct child");
  return children;
}

function Consumer() {
  console.log("consumer");
  const [state, setState] = React.useContext(MyContext);
  return (
    <div>
      <span>State value: {state}</span>
      <br />
      <button onClick={() => setState(old => old + "Yes")}>Change</button>
    </div>
  );
}

The App

export default function App() {
  console.log("APP");
  return (
    <Provider>
      <DirectChild>
        <Consumer />
      </DirectChild>
    </Provider>
  );
}

Here is a codesandbox demo

like image 340
Incepter Avatar asked Sep 02 '25 06:09

Incepter


1 Answers

Found the reason,

There is no bug, and no strange behavior, I was just missing that codesandbox by default renders the App within a React.StrictMode parent.

First, I ported the code to my local project, then observed that there was no issues.

Searched over codesandbox repo issues and found that it is linked to a react behavior on strict mode:

It is expected that setState updaters will run twice in strict mode in development. This helps ensure the code doesn't rely on them running a single time (which wouldn't be the case if an async render was aborted and alter restarted). If your setState updaters are pure functions (as they should be) then this shouldn't affect the logic of your application.

Codesandbox issue

React issue

But still, The effect is executed only once and misses my ref update.

EDIT

React 18 introduced strict effects, which runs effects also twice under strict mode in development.

like image 183
Incepter Avatar answered Sep 04 '25 20:09

Incepter