Skip to content

[devtools] Prevent incorrect render detection for user components in didFiberRender (#33423) #33434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

developerjhp
Copy link

Summary

Fixes false positive rendering detection in React DevTools Profiler by improving the didFiberRender function to accurately detect when user components actually re-render, preventing misleading "The parent component rendered" messages.

Problem

Previously, React DevTools would incorrectly mark components as "rendered" even when they didn't actually re-render due to bailouts. This happened because the didFiberRender function only checked the PerformedWork flag, but React can set this flag even during bailout scenarios.

Example scenario:

  • Parent component state changes
  • Sibling component with unchanged props shows "The parent component rendered"
  • But the sibling component console.log shows it didn't actually re-render

Solution

Enhanced didFiberRender function for user components (ClassComponent, FunctionComponent, etc.):

// Before
const PerformedWork = 0b000000000000000000000000001;
return (getFiberFlags(nextFiber) & PerformedWork) === PerformedWork;

// After  
if ((getFiberFlags(nextFiber) & PerformedWork) === 0) {
  return false;
}
if (
  prevFiber != null &&
  prevFiber.memoizedProps === nextFiber.memoizedProps &&
  prevFiber.memoizedState === nextFiber.memoizedState &&
  prevFiber.ref === nextFiber.ref
) {
  // React may mark PerformedWork even if we bailed out. Double check
  // that inputs actually changed before reporting a render.
  return false;
}
return true;

This change ensures that:

  1. We first check the PerformedWork flag (performance optimization)
  2. Then verify that props/state/ref actually changed (accuracy check)
  3. Only report rendering when inputs genuinely changed

Testing

Test Setup:
Used the following test case with independent Count and Greeting components:

const Count = () => {
    const [count, setCount] = useState(0);
    console.log('Count Component Rerendered');
    return (
        <button onClick={() => setCount(c => c + 1)}>
            Count: {count}
        </button>
    );
};

const Greeting = () => {
    console.log('Greeting Component Rerendered');
    return <span>Hello World!</span>;
};

const App = () => {
    const [appState, setAppState] = useState(0);
    console.log('App Component Rerendered');
    
    return (
        <main>
            <div>App State: {appState}</div>
            <button onClick={() => setAppState(s => s + 1)}>
                App Rerender Trigger (All children rerender)
            </button>
            <hr />
            <Count />
            <div>
                <Greeting />
            </div>
        </main>
    );
};

Test Results:
Tested and verified with this code

// Before

Screen.Recording.2025-06-04.at.13.17.03.mov

// After

Screen.Recording.2025-06-04.at.13.17.35.mov

Before Fix:

  • Click Count button → Console shows only "Count Component Rerendered"
  • DevTools Profiler → Greeting component incorrectly shows "The parent component rendered"

After Fix:

  • Click Count button → Console shows only "Count Component Rerendered"
  • DevTools Profiler → Greeting component correctly shows no rendering information

Related

This change specifically targets user components (Function/Class components) and maintains existing behavior for host components, ensuring accurate rendering detection across the React component tree.

Fixes #33423 , #19732

Improve rendering detection accuracy by adding actual input change verification
for user components that have PerformedWork flag set. This prevents showing
"The parent component rendered" message and highlight updates for components
that didn't actually re-render due to bailouts.

- Add props/state/ref comparison for user components after PerformedWork check
- Restore original props comparison logic for host components
- Fixes issue where bailout components were incorrectly marked as rendered
@michaelboyles
Copy link

I tested your solution and it seems to work. Adding this to the top of the function instead seems to achieve the same thing. Not sure if they are practically equivalent or not

if (prevFiber === nextFiber) {
    return false;
}

@hoxyq hoxyq self-requested a review June 4, 2025 12:24
@developerjhp
Copy link
Author

@michaelboyles

Thanks for testing it out!

You're right — prevFiber === nextFiber can short-circuit some cases and slightly improve performance.
However, it's not functionally equivalent to checking memoizedProps, memoizedState, and ref,
since React may sometimes create a new fiber even when nothing actually changed.

So while it's a great optimization to add on top, it can't fully replace the deeper equality check.
We could consider including both, to balance correctness and performance.

Open to thoughts on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug: profiler incorrectly reports 'The parent component rendered'
3 participants