Skip to content

Conversation

@leeguooooo
Copy link

@leeguooooo leeguooooo commented Dec 29, 2025

Summary

  • Handle status-thenable transitions in lazy initializer, including pending→settled flips.
  • Keep async debug info (ioInfo) in sync with status-only thenables.
  • Add tests for fulfilled/rejected status thenables and pending→fulfilled flip.

Testing

  • NODE_ENV=development RELEASE_CHANNEL=experimental compactConsole=false node ./scripts/jest/jest.js --config ./scripts/jest/config.source.js --runTestsByPath packages/react-reconciler/src/tests/ReactLazy-test.internal.js --runInBand

Fixes #35399

@meta-cla meta-cla bot added the CLA Signed label Dec 29, 2025
@leeguooooo leeguooooo force-pushed the fix-35399-lazy-thenable-race branch from 3b3cfe6 to 46604ca Compare December 29, 2025 03:06
@leeguooooo leeguooooo marked this pull request as ready for review December 29, 2025 04:44
@ApsiV11
Copy link

ApsiV11 commented Jan 2, 2026

This PR seems to also fix useId causing hydration issues. At least vercel/next.js#84029 and #35210 would be fixed.

Copy link
Collaborator

@acdlite acdlite left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. Great fix. Do you think you'd be able to write a regression test that reproduces the useId error you're referencing? (If not, I think it's fine to land as is.)

@react-sizebot
Copy link

react-sizebot commented Jan 14, 2026

Comparing: 4a3d993...5027732

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.84 kB 6.84 kB +0.05% 1.88 kB 1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js +0.01% 608.03 kB 608.10 kB +0.02% 107.61 kB 107.63 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.84 kB 6.84 kB +0.05% 1.88 kB 1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 667.26 kB 667.32 kB = 117.51 kB 117.52 kB
facebook-www/ReactDOM-prod.classic.js = 693.38 kB 693.44 kB = 122.00 kB 122.01 kB
facebook-www/ReactDOM-prod.modern.js = 683.76 kB 683.83 kB = 120.40 kB 120.41 kB

Significant size changes

Includes any change greater than 0.2%:

(No significant changes)

Generated by 🚫 dangerJS against 5027732

Copy link
Collaborator

@acdlite acdlite left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually nvm I think we need to add a regression test before landing this

@eps1lon
Copy link
Collaborator

eps1lon commented Jan 14, 2026

The failing devtools test is also sus though it might just be a missing await and switch to async act.

Copy link
Collaborator

@gnoff gnoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think #35518 will fix the useId hydration issue. I do not think this change necessarily addressed the root cause but because RSC lazy elements often resolve synchronously the optimization in this PR to check the status again before throwing caused the bug in affected test cases to be hidden. However RSC does not guarantee that the lazy nodes will resolve sync b/c they may still be waiting on a microtask (or longer) so it is possible that the underlying bug could still be expressed albeit less often.

The changes in this PR may still be worthwhile (sync resolution of lazy's might be perf positive for RSC lazy elements). But the fact that the thenable state in this PR gets created over and over doesn't seem right and so the way to do this is more involved. The extra machinery in trackUsedThenableState is probably unecessary and we can jsut store what is required on the lazy iself.

A minimal change would maybe be

x.then(noop, noop)
if (lazyType._payload._status === INITIALIZED) return lazyType._payload._value

should probably put that in ReactLazy though

@leeguooooo leeguooooo force-pushed the fix-35399-lazy-thenable-race branch from 918b052 to 3c51c4b Compare January 16, 2026 07:57
@gnoff
Copy link
Collaborator

gnoff commented Jan 16, 2026

I tried adding a test to show what sync resolution of lazy would look like but it should already be supported. when the lazy is in uninitialized status it will call .then on the returned thenable. If this runs sync then the lazy status will become Resolved before we fall through to the Resolved check below. So now I am less certain why this change actually helped with the useId change. Regardless since the underlying useId issue was addressed in #35518 I think we don't need this change anymore

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: resolveLazy catching promises causes race condition with short-lived Suspense-wrapped components

6 participants