react
react copied to clipboard
[DevTools Bug]: Phantom re-renders on sibling <label> components
Website or app
https://stackblitz.com/edit/react-devtools-bug?file=src%2FApp.jsx
Repro steps
- Start profiler
- Input into "Component with state"
- Both of "ComponentWithState" and "AnotherReactComponent" were re-rendered will be shown in the report but why?
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)
No response
Error component stack (automated)
No response
GitHub query string (automated)
No response
I have been able to reproduce this issue too with very similar simple examples
Bumped.
I also can easily reproduce this with a Vite template with [email protected] and [email protected].
Run npm create vite and change App.jsx like following:
const ViteIcon = () => {
return (
<div><a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a></div>
)
}
const ReactIcon = () => {
return (
<div>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
)
}
const Counter = () => {
const [count, setCount] = useState(0)
return (
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
)
}
function App() {
return (
<>
<div>
<ViteIcon />
<ReactIcon />
</div>
<Counter />
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
Now everytime I click the button, both ReactIcon and ViteIcon are re-rendered but they should not, right?
(I use Highlight updates when components render. option to check this, not a profiler.)
Weird thing is, when you remove div around ReactIcon and ViteIcon these re-rendering won't happen.
I'm sure I hadn't observed this behaviour a month ago. I'm really confused about this 😕
Can anyone shed light on this?
Here is an additional Info.
This happens on Chrome Extension DevTools version: 6.0.1-c7c68ef842 and Firefox addon DevTools version: 6.0.0-d66fa02a30.
But not on Firefox addon DevTools version: 5.3.1-ccb20cb88b.
I hope this might be a bit of help.
Here is an additional Info.
This happens on Chrome Extension DevTools version: 6.0.1-c7c68ef842 and Firefox addon DevTools version: 6.0.0-d66fa02a30.
But not on Firefox addon DevTools version: 5.3.1-ccb20cb88b.
I hope this might be a bit of help.
Any idea about version of Chrome Extension without this problem?
Any idea about version of Chrome Extension without this problem?
Unfortunately no, I don't keep old Chrome environments.
No team members care about dev tools now?
BTW, react-scan could be a good alternative.
https://github.com/aidenybai/react-scan
I have difficulties to see re-rendering flash though 😅
This is the same issue as in https://github.com/facebook/react/issues/31353 caused by the way how fragments are handled during re-rendering. See my comment in the linked issue for details.
@V3RON I see this issue or a similar one when using only a single child in the updated component. React DevTools shows re-rendering for sibling components when no re-rendering seems to have occurred.
const Rerenders = () => {
const [increment, setIncrement] = useState(0);
useEffect(() => {
requestAnimationFrame(() => {
setIncrement(increment + 1);
});
}, [increment]);
return <p>I rerender all of the time</p>;
};
const ShouldNotRerender = () => {
console.log(
"ShouldNotRerender actually rendered" // Appears 2 times per direct or memoized use with strict mode, never any more
);
return <p>I should not rerender</p>;
};
const ShouldNotRerenderMemo = React.memo(ShouldNotRerender);
export default function App() {
return (
<div>
{/* 🔃 Should rerender */}
<Rerenders />
{/* ✅ Okay */}
<ShouldNotRerender />
{/* ✅ Okay */}
<ShouldNotRerenderMemo />
<div>
{/* ⚠️ DevTools shows rerenders */}
<ShouldNotRerender />
{/* ⚠️ DevTools shows rerenders */}
<ShouldNotRerenderMemo />
</div>
</div>
);
}
The profiler shows a hook change in <Rerenders> (accurate), the first <ShouldNotRerender> and its memoized version not rerendering (accurate), but then it shows <ShouldNotRerender> and its memoized version rerendering when inside the <div> (not accurate).
The profiler claims it's due to the parent component rerendering, but <App> didn't rerender, and the console.log() doesn't fire outside of initial render from any of the <ShouldNotRerender> uses.
So I looked to see what the profiler would say about the direct parent <div>, and interestingly enough, everything changes when trying to run the profiler unfiltered.
- Run the profiler with
Hide components where... type equals dom nodes (e.g. <div>)enabled (required for some reason, another bug?) - Run the profile again with the filter disabled
During and after the unfiltered profiler run, the overlay will show you just p is re-rendering, which doesn't feel technically correct to me, but at least it's the right component.
In fact, the profiler for the unfiltered run doesn't even have <Rerenders>'s <p> tag, and attributes the rerender to <Rerenders> due to the hook change (accurate).
So the overlay doesn't feel accurate, but on the positive side, the profiler is now accurate! <ShouldNotRerender> (memoized or not) and its parent <div> (if any) do not show any rerendering. This makes me believe that some phantom rerenders are arise from the filtering logic or something adjacent in DevTools.
Upon a refresh, the overlay will only show React components again even if you profile with the filter disabled until you profile with it enabled and then disabled.
facing the same issue...