From c1cbc1e6300cc56c120b902e00527d75aa1df50e Mon Sep 17 00:00:00 2001 From: developerjhp Date: Tue, 8 Jul 2025 15:45:21 +0900 Subject: [PATCH 1/2] [devtools]: Prevent false positive render detection in profiler (#33423) - This commit addresses a bug in React DevTools where the profiler incorrectly reported user components as having re-rendered with the message "The parent component rendered," e ven when they had not. This typically occurred for sibling components (like in the reported issue) nested under a (e.g., ) when the parent re-rendered. - Problem: The function, which determines if a component has rendered, was being called for components whose Fiber object remained referentially identical (). While correctly checks for changes in , , and , the issue stemmed from the fact that it was invoked at all for these unchanged components, leading to misleading "render" indications in the profiler a nd highlight updates. The core problem was not the logic "within" , but rather the conditions under which it was "called". - Solution: To resolve this, a conditional check has been added within the function, which is responsible for traversing and updating the Fiber tree. Before calling for user components (Function, Class, Memo, ForwardRef components), the system now verifies two key conditions: 1. : The Fiber object itself has not changed (i.e., it's the same instance). 2. is a or : The component's direct parent is a host element (like a ). If both conditions are met, it indicates that the component has not genuinely re-rendered, and is explicitly set to . This prevents from being called unnecessarily and avoids the false positive reporting in the DevTools profiler and render highlighting. The same logic is applied when checking if has updated, ensuring consistency. - This change significantly improves the accuracy of the React DevTools profiler and render highlighting, providing developers with more reliable information about component re-renders and bailouts. Fixes #33423 Relates to #33434 --- .../src/backend/fiber/renderer.js | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index e24734b0ab032..0d6a789c63e4a 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -3313,10 +3313,23 @@ export function attach( elementType === ElementTypeForwardRef ) { // Otherwise if this is a traced ancestor, flag for the nearest host descendant(s). - traceNearestHostComponentUpdate = didFiberRender( - prevFiber, - nextFiber, - ); + // Add a condition to prevent calling didFiberRender if the fiber itself hasn't changed + // and its parent is a HostComponent (like a div). + // This addresses the false positive where a parent HostComponent re-renders, + // but its child FunctionComponent (like Greeting) does not actually re-render itself. + if ( + prevFiber === nextFiber && + nextFiber.return != null && + (nextFiber.return.tag === HostComponent || nextFiber.return.tag === HostSingleton) + ) { + traceNearestHostComponentUpdate = false; // No actual render for this component + } else { + // Otherwise if this is a traced ancestor, flag for the nearest host descendant(s). + traceNearestHostComponentUpdate = didFiberRender( + prevFiber, + nextFiber, + ); + } } } } @@ -3330,6 +3343,13 @@ export function attach( if ( mostRecentlyInspectedElement !== null && mostRecentlyInspectedElement.id === fiberInstance.id && + // Add the same condition here to prevent unnecessary re-inspection + // if the fiber itself hasn't changed and its parent is a HostComponent. + !( + prevFiber === nextFiber && + nextFiber.return != null && + (nextFiber.return.tag === HostComponent || nextFiber.return.tag === HostSingleton) + ) && didFiberRender(prevFiber, nextFiber) ) { // If this Fiber has updated, clear cached inspected data. From 92d83a62a04ba990a0de6eba99188b2f8a583219 Mon Sep 17 00:00:00 2001 From: developerjhp Date: Wed, 23 Jul 2025 16:31:29 +0900 Subject: [PATCH 2/2] [devtools] Prevent false positive render detection for components under filtered HostComponents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit addresses a bug in React DevTools where sibling user components were incorrectly reported as having re-rendered when they were nested under HostComponents (like
) that get filtered out by the DevTools component tree. ## Problem When a user component (e.g., Count) triggers a re-render within a HostComponent container, DevTools would incorrectly report sibling components (e.g., Greeting) as having re-rendered, even when their Fiber objects remained referentially identical (prevFiber === nextFiber). This created misleading information in: - Profiler data collection (false entries in commit data) - Render highlighting (incorrect visual indicators) - Inspector cache management (unnecessary invalidation) ## Root Cause The issue occurred because didFiberRender() was being called for components whose Fiber objects hadn't actually changed, but were children of HostComponents that were being processed during reconciliation. Since HostComponents are filtered out by default in DevTools, the relationship between unchanged sibling components wasn't being properly recognized. │ ## Solution Instead of modifying the didFiberRender() function itself, this fix prevents the function from being called when we can determine that a component hasn't actually re-rendered. The key insight is that if: - prevFiber === nextFiber (same Fiber object reference) - nextFiber.return is a HostComponent or HostSingleton Then the component didn't actually re-render and didFiberRender() shouldn't be invoked. --- .../src/backend/fiber/renderer.js | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index 0d6a789c63e4a..18cc4cf8e65c9 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -2813,7 +2813,19 @@ export function attach( pushOperation(convertedTreeBaseDuration); } - if (prevFiber == null || didFiberRender(prevFiber, fiber)) { + // Prevent false positive render detection when identical Fiber objects + // are nested under HostComponents (like div) that get filtered out by DevTools. + // This addresses cases where a parent HostComponent re-renders but its child + // components (like function components) remain referentially identical and + // should not be reported as having re-rendered in profiler data collection. + if ( + prevFiber == null || + (!( + prevFiber === fiber && + fiber.return != null && + (fiber.return.tag === HostComponent || fiber.return.tag === HostSingleton) + ) && didFiberRender(prevFiber, fiber)) + ) { if (actualDuration != null) { // The actual duration reported by React includes time spent working on children. // This is useful information, but it's also useful to be able to exclude child durations. @@ -3312,11 +3324,12 @@ export function attach( elementType === ElementTypeMemo || elementType === ElementTypeForwardRef ) { - // Otherwise if this is a traced ancestor, flag for the nearest host descendant(s). - // Add a condition to prevent calling didFiberRender if the fiber itself hasn't changed - // and its parent is a HostComponent (like a div). - // This addresses the false positive where a parent HostComponent re-renders, - // but its child FunctionComponent (like Greeting) does not actually re-render itself. + // Prevent false positive render highlighting when identical Fiber objects + // are nested under HostComponents that get filtered out by DevTools. + // When a HostComponent (like div) re-renders but its child components + // remain referentially identical (prevFiber === nextFiber), those children + // should not be highlighted as having re-rendered since they were effectively + // skipped during React's reconciliation process. if ( prevFiber === nextFiber && nextFiber.return != null && @@ -3343,8 +3356,10 @@ export function attach( if ( mostRecentlyInspectedElement !== null && mostRecentlyInspectedElement.id === fiberInstance.id && - // Add the same condition here to prevent unnecessary re-inspection - // if the fiber itself hasn't changed and its parent is a HostComponent. + // Prevent unnecessary inspector cache invalidation when identical Fiber objects + // are nested under HostComponents that get filtered out by DevTools. + // When a component hasn't actually re-rendered (same Fiber reference under a HostComponent), + // we should preserve the cached inspection data to avoid unnecessary re-computation. !( prevFiber === nextFiber && nextFiber.return != null &&