Skip to content

fix(react): count only fibers that rendered in react renders profiler - #2512

Open
sx4im wants to merge 2 commits into
vercel-labs:mainfrom
sx4im:fix-2508-react-renders-untouched-fibers
Open

sx4im wants to merge 2 commits into
vercel-labs:mainfrom
sx4im:fix-2508-react-renders-untouched-fibers

Conversation

@sx4im

@sx4im sx4im commented Nov 9, 2026

Copy link
Copy Markdown

react renders counted components that never rendered in a commit. It walked the whole tree and counted a fiber when any flag was set, or when props or state differed from its alternate. Flags from an earlier render stay on fibers React bailed out on, and a fiber never updated since mount has no alternate, so untouched components were re-counted on every commit and mounts were reported again and again.

Count a fiber only when it ran in this commit, the way React DevTools does (didFiberRender): gate on the PerformedWork flag, and descend into a subtree only when its child pointer changed since the previous commit. On mount there is no alternate yet, so the whole tree is walked and mounts still count once.

How verified: ran the profiler scripts in Node against simulated React 19 fiber trees for the repro in the issue (mount plus 10 ticks). Ticker gets 10 re-renders, StaticList and Static get 0. The same harness against the old code reproduces the bug (StaticList 10 re-renders, Static 50 extra mounts). Added a unit test pinning the new rule, and cargo fmt --check is clean. The full Rust test run is left to CI.

Fixes #2508

The profiler walked the whole tree on every commit and counted a fiber
when any flag was set or when props or state differed from its alternate.
Flags from an earlier render stay on fibers React bailed out on, and a
fiber never updated since mount has no alternate at all, so untouched
components were re-counted on every commit and fresh mounts were reported
again and again.

Count a fiber only when it ran in this commit, the way React DevTools
does (didFiberRender): gate on the PerformedWork flag, and descend into a
subtree only when its child pointer changed since the previous commit.
On mount there is no alternate yet, so the whole tree is walked and
mounts are still counted once.

Fixes vercel-labs#2508
@vercel

vercel Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

@sx4im is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Comment thread cli/src/native/react/scripts.rs Resolved
React 16 stores effect flags in `effectTag`; React 17 renamed the field
to `flags`. The renders profiler gated counting on `fiber.flags` only,
so on a React 16 page every post-mount render was dropped: `fiber.flags`
is undefined there and the PerformedWork check never fired. The old code
still counted those renders through its props/state comparison, so this
is a regression for React 16 pages.

Read the flags through a small helper that returns `fiber.flags` when
present and falls back to `fiber.effectTag`. The bit values are the same
in both versions. Applies to the render gate and to the DOM-mutation
check, which reads the same field.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

react renders counts components that did not render: untouched fibers are re-counted on every commit

1 participant