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.
- Created a context, then a component which renders its provider.
- The provider provides a state value and its setter.
- Created a direct child that renders directly its children.
- Created a consumer which reads the context value and adds a button to change it.
- Each component executes a console.log when it renders.
- Added an effect to the provider without deps to log every time it renders.
- 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