-
Notifications
You must be signed in to change notification settings - Fork 50k
Description
Website or app
https://github.com/mukesharyal/react-devtools-bug-report
Repro steps
- Clone the GitHub repo and run
npm install - Run the local dev server with
npm run devand also run React Devtools - You will see two components
ComponentAandComponentB - Cause a re-render of
ComponentAby clicking the button a bunch of times ComponentBwill also be shown in the component render highlights andProfilersessions
The structure of App component looks like this:
import ComponentA from './components/ComponentA';
import ComponentB from './components/ComponentB';
function App() {
return(
<div>
<div>
<ComponentA />
</div>
<div>
<ComponentB />
</div>
</div>
);
}
export default App;
This is what ComponentA looks like:
import { useState } from "react";
export default function ComponentA()
{
console.log("Component A rendered!");
const [count, setCount] = useState(0);
return(
<>
<h1>
Component A
</h1>
<h2>
Count is {count}
</h2>
<button onClick={() => { setCount(count => count + 1) }}>
Increase Count
</button>
</>
)
}
And here is ComponentB:
export default function ComponentB()
{
console.log("Component B rendered!");
return(
<>
<h1>
Component B
</h1>
</>
)
}
Now, when I re-render ComponentA by clicking the button a bunch of times, I see re-render highlights in ComponentA. But, the surprising part is that I also see highlights in ComponentB.
Here are the highlights:
And here is a Profiler session I recorded:
It clearly shwos that ComponentB also "re-rendered" a bunch of times, and it actually took more time to "re-render" ComponentB than ComponentA.
And here is what the console logs look like:
And indeed, like we expect, there is only the console log when ComponentB rendered the first time and not again. But, the Profiler session and the component render highlights say otherwise.
Now, let's change the structure of App component just a little bit:
import ComponentA from './components/ComponentA';
import ComponentB from './components/ComponentB';
function App() {
return(
<div>
<div>
<ComponentA />
</div>
<ComponentB />
<div>
</div>
</div>
);
}
export default App;
And now let's see the component highlights and Profiler session.
Here is the highlights now:
And here is what the Profiler says:
So, now the results are what we expect.
This project was made with React 19, but we also see the same things with React 18 as well. And, the presence or absence of the React Compiler makes no difference.
To verify that the effects are not seen with composition, I made a simple Wrapper component and had the same divs wrap our components as before. Here is the Wrapper:
export default function Wrapper({ children })
{
return(
<div>
{ children }
</div>
)
}
And now, we use the Wrapper instead of divs in our App component like this:
import Wrapper from './components/Wrapper';
import ComponentA from './components/ComponentA';
import ComponentB from './components/ComponentB';
function App() {
return(
<Wrapper>
<Wrapper>
<ComponentA />
</Wrapper>
<Wrapper>
<ComponentB />
</Wrapper>
</Wrapper>
);
}
export default App;
Thankfully, we do not see the weird behavior in this case. Sigh!
So after all this, we clearly see that the weird stuffs are only seen in a specific case where we are using divs before the composition for layout purposes.
But, I do think that this is not at all "expected React behavior". Either this is a bug with the React library itself, or the React Devtools are lying to us.
So, that was the weird thing I saw. I have also written a blog post where I discuss this same thing in more detail. It can be found here.
How often does this bug happen?
Every time
DevTools package (automated)
No response
DevTools version (automated)
No response
Error message (automated)
No response
Error call stack (automated)
Error component stack (automated)
GitHub query string (automated)