-
Notifications
You must be signed in to change notification settings - Fork 50k
Fix: uDV skipped initial value if earlier transition suspended #34376
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
Conversation
|
Comparing: 7697a9f...a45a354 Critical size changesIncludes critical production bundles, as well as any change greater than 2%:
Significant size changesIncludes any change greater than 0.2%: (No significant changes) |
rickhanlonii
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sick find
Fixes a bug in useDeferredValue's optional `initialValue` argument. In the regression case, if a new useDeferredValue hook is mounted while an earlier transition is suspended, the `initialValue` argument of the new hook was ignored. After the fix, the `initialValue` argument is correctly rendered during the initial mount, regardless of whether other transitions were suspended. The culprit was related to the mechanism we use to track whether a render is the result of a `useDeferredValue` hook: we assign the deferred lane a TransitionLane, then entangle that lane with the DeferredLane bit. During the subsequent render, we check for the presence of the DeferredLane bit to determine whether to switch to the final, canonical value. But because transition lanes can themselves become entangled with other transitions, the effect is that every entangled transition was being treated as if it were the result of a `useDeferredValue` hook, causing us to skip the initial value and go straight to the final one. The fix I've chosen is to reserve some subset of TransitionLanes to be used only for deferred work, instead of using entanglement. This is similar to how retries are already implemented. Originally I tried not to implement it this way because it means there are now slightly fewer lanes allocated for regular transitions, but I underestimated how similar deferred work is to retries; they end up having a lot of the same requirements. Eventually it may be possible to merge the two concepts.
f61b963 to
a45a354
Compare
Fixes a bug in useDeferredValue's optional `initialValue` argument. In the regression case, if a new useDeferredValue hook is mounted while an earlier transition is suspended, the `initialValue` argument of the new hook was ignored. After the fix, the `initialValue` argument is correctly rendered during the initial mount, regardless of whether other transitions were suspended. The culprit was related to the mechanism we use to track whether a render is the result of a `useDeferredValue` hook: we assign the deferred lane a TransitionLane, then entangle that lane with the DeferredLane bit. During the subsequent render, we check for the presence of the DeferredLane bit to determine whether to switch to the final, canonical value. But because transition lanes can themselves become entangled with other transitions, the effect is that every entangled transition was being treated as if it were the result of a `useDeferredValue` hook, causing us to skip the initial value and go straight to the final one. The fix I've chosen is to reserve some subset of TransitionLanes to be used only for deferred work, instead of using entanglement. This is similar to how retries are already implemented. Originally I tried not to implement it this way because it means there are now slightly fewer lanes allocated for regular transitions, but I underestimated how similar deferred work is to retries; they end up having a lot of the same requirements. Eventually it may be possible to merge the two concepts. DiffTrain build for [3302d1f](3302d1f)
Fixes a bug in useDeferredValue's optional `initialValue` argument. In the regression case, if a new useDeferredValue hook is mounted while an earlier transition is suspended, the `initialValue` argument of the new hook was ignored. After the fix, the `initialValue` argument is correctly rendered during the initial mount, regardless of whether other transitions were suspended. The culprit was related to the mechanism we use to track whether a render is the result of a `useDeferredValue` hook: we assign the deferred lane a TransitionLane, then entangle that lane with the DeferredLane bit. During the subsequent render, we check for the presence of the DeferredLane bit to determine whether to switch to the final, canonical value. But because transition lanes can themselves become entangled with other transitions, the effect is that every entangled transition was being treated as if it were the result of a `useDeferredValue` hook, causing us to skip the initial value and go straight to the final one. The fix I've chosen is to reserve some subset of TransitionLanes to be used only for deferred work, instead of using entanglement. This is similar to how retries are already implemented. Originally I tried not to implement it this way because it means there are now slightly fewer lanes allocated for regular transitions, but I underestimated how similar deferred work is to retries; they end up having a lot of the same requirements. Eventually it may be possible to merge the two concepts. DiffTrain build for [3302d1f](3302d1f)
[diff facebook/react@2805f0ed...3302d1f7](facebook/react@2805f0e...3302d1f) <details> <summary>React upstream changes</summary> - facebook/react#34376 - facebook/react#34375 - facebook/react#34373 </details>
sebmarkbage
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably worth just making this use the Retry lane like we initially thought since they're conceptually very similar. The idea is to commit the first Transition basically instantly and then have a follow up to fill in the rest.
It's kind of interesting how this should be visualized in the Performance Track but I think it would actually make sense to treat this case a the Suspense track (Retry) instead of the Transition track since it's really about tracking an update which might itself act as a thing that resolves a Suspense boundary. So arguably it's confusing to visualize this as a Transition like it would be now.
…ook#34376) Fixes a bug in useDeferredValue's optional `initialValue` argument. In the regression case, if a new useDeferredValue hook is mounted while an earlier transition is suspended, the `initialValue` argument of the new hook was ignored. After the fix, the `initialValue` argument is correctly rendered during the initial mount, regardless of whether other transitions were suspended. The culprit was related to the mechanism we use to track whether a render is the result of a `useDeferredValue` hook: we assign the deferred lane a TransitionLane, then entangle that lane with the DeferredLane bit. During the subsequent render, we check for the presence of the DeferredLane bit to determine whether to switch to the final, canonical value. But because transition lanes can themselves become entangled with other transitions, the effect is that every entangled transition was being treated as if it were the result of a `useDeferredValue` hook, causing us to skip the initial value and go straight to the final one. The fix I've chosen is to reserve some subset of TransitionLanes to be used only for deferred work, instead of using entanglement. This is similar to how retries are already implemented. Originally I tried not to implement it this way because it means there are now slightly fewer lanes allocated for regular transitions, but I underestimated how similar deferred work is to retries; they end up having a lot of the same requirements. Eventually it may be possible to merge the two concepts.
…ook#34376) Fixes a bug in useDeferredValue's optional `initialValue` argument. In the regression case, if a new useDeferredValue hook is mounted while an earlier transition is suspended, the `initialValue` argument of the new hook was ignored. After the fix, the `initialValue` argument is correctly rendered during the initial mount, regardless of whether other transitions were suspended. The culprit was related to the mechanism we use to track whether a render is the result of a `useDeferredValue` hook: we assign the deferred lane a TransitionLane, then entangle that lane with the DeferredLane bit. During the subsequent render, we check for the presence of the DeferredLane bit to determine whether to switch to the final, canonical value. But because transition lanes can themselves become entangled with other transitions, the effect is that every entangled transition was being treated as if it were the result of a `useDeferredValue` hook, causing us to skip the initial value and go straight to the final one. The fix I've chosen is to reserve some subset of TransitionLanes to be used only for deferred work, instead of using entanglement. This is similar to how retries are already implemented. Originally I tried not to implement it this way because it means there are now slightly fewer lanes allocated for regular transitions, but I underestimated how similar deferred work is to retries; they end up having a lot of the same requirements. Eventually it may be possible to merge the two concepts. DiffTrain build for [3302d1f](facebook@3302d1f)
…ook#34376) Fixes a bug in useDeferredValue's optional `initialValue` argument. In the regression case, if a new useDeferredValue hook is mounted while an earlier transition is suspended, the `initialValue` argument of the new hook was ignored. After the fix, the `initialValue` argument is correctly rendered during the initial mount, regardless of whether other transitions were suspended. The culprit was related to the mechanism we use to track whether a render is the result of a `useDeferredValue` hook: we assign the deferred lane a TransitionLane, then entangle that lane with the DeferredLane bit. During the subsequent render, we check for the presence of the DeferredLane bit to determine whether to switch to the final, canonical value. But because transition lanes can themselves become entangled with other transitions, the effect is that every entangled transition was being treated as if it were the result of a `useDeferredValue` hook, causing us to skip the initial value and go straight to the final one. The fix I've chosen is to reserve some subset of TransitionLanes to be used only for deferred work, instead of using entanglement. This is similar to how retries are already implemented. Originally I tried not to implement it this way because it means there are now slightly fewer lanes allocated for regular transitions, but I underestimated how similar deferred work is to retries; they end up having a lot of the same requirements. Eventually it may be possible to merge the two concepts. DiffTrain build for [3302d1f](facebook@3302d1f)
This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react) ([source](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react)) | [`19.1.10` -> `19.2.6`](https://renovatebot.com/diffs/npm/@types%2freact/19.1.10/19.2.6) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-dom) ([source](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom)) | [`19.1.7` -> `19.2.3`](https://renovatebot.com/diffs/npm/@types%2freact-dom/19.1.7/19.2.3) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [react](https://react.dev/) ([source](https://github.com/facebook/react/tree/HEAD/packages/react)) | [`19.1.1` -> `19.2.0`](https://renovatebot.com/diffs/npm/react/19.1.1/19.2.0) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [react-dom](https://react.dev/) ([source](https://github.com/facebook/react/tree/HEAD/packages/react-dom)) | [`19.1.1` -> `19.2.0`](https://renovatebot.com/diffs/npm/react-dom/19.1.1/19.2.0) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>facebook/react (react)</summary> ### [`v19.2.0`](https://github.com/facebook/react/blob/HEAD/CHANGELOG.md#1920-October-1st-2025) [Compare Source](facebook/react@v19.1.1...v19.2.0) Below is a list of all new features, APIs, and bug fixes. Read the [React 19.2 release post](https://react.dev/blog/2025/10/01/react-19-2) for more information. ##### New React Features - [`<Activity>`](https://react.dev/reference/react/Activity): A new API to hide and restore the UI and internal state of its children. - [`useEffectEvent`](https://react.dev/reference/react/useEffectEvent) is a React Hook that lets you extract non-reactive logic into an [Effect Event](https://react.dev/learn/separating-events-from-effects#declaring-an-effect-event). - [`cacheSignal`](https://react.dev/reference/react/cacheSignal) (for RSCs) lets your know when the `cache()` lifetime is over. - [React Performance tracks](https://react.dev/reference/dev-tools/react-performance-tracks) appear on the Performance panel’s timeline in your browser developer tools ##### New React DOM Features - Added resume APIs for partial pre-rendering with Web Streams: - [`resume`](https://react.dev/reference/react-dom/server/resume): to resume a prerender to a stream. - [`resumeAndPrerender`](https://react.dev/reference/react-dom/static/resumeAndPrerender): to resume a prerender to HTML. - Added resume APIs for partial pre-rendering with Node Streams: - [`resumeToPipeableStream`](https://react.dev/reference/react-dom/server/resumeToPipeableStream): to resume a prerender to a stream. - [`resumeAndPrerenderToNodeStream`](https://react.dev/reference/react-dom/static/resumeAndPrerenderToNodeStream): to resume a prerender to HTML. - Updated [`prerender`](https://react.dev/reference/react-dom/static/prerender) APIs to return a `postponed` state that can be passed to the `resume` APIs. ##### Notable changes - React DOM now batches suspense boundary reveals, matching the behavior of client side rendering. This change is especially noticeable when animating the reveal of Suspense boundaries e.g. with the upcoming `<ViewTransition>` Component. React will batch as much reveals as possible before the first paint while trying to hit popular first-contentful paint metrics. - Add Node Web Streams (`prerender`, `renderToReadableStream`) to server-side-rendering APIs for Node.js - Use underscore instead of `:` IDs generated by useId ##### All Changes ##### React - `<Activity />` was developed over many years, starting before `ClassComponent.setState` ([@​acdlite](https://github.com/acdlite) [@​sebmarkbage](https://github.com/sebmarkbage) and many others) - Stringify context as "SomeContext" instead of "SomeContext.Provider" ([@​kassens](https://github.com/kassens) [#​33507](facebook/react#33507)) - Include stack of cause of React instrumentation errors with `%o` placeholder ([@​eps1lon](https://github.com/eps1lon) [#​34198](facebook/react#34198)) - Fix infinite `useDeferredValue` loop in popstate event ([@​acdlite](https://github.com/acdlite) [#​32821](facebook/react#32821)) - Fix a bug when an initial value was passed to `useDeferredValue` ([@​acdlite](https://github.com/acdlite) [#​34376](facebook/react#34376)) - Fix a crash when submitting forms with Client Actions ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33055](facebook/react#33055)) - Hide/unhide the content of dehydrated suspense boundaries if they resuspend ([@​sebmarkbage](https://github.com/sebmarkbage) [#​32900](facebook/react#32900)) - Avoid stack overflow on wide trees during Hot Reload ([@​sophiebits](https://github.com/sophiebits) [#​34145](facebook/react#34145)) - Improve Owner and Component stacks in various places ([@​sebmarkbage](https://github.com/sebmarkbage), [@​eps1lon](https://github.com/eps1lon): [#​33629](facebook/react#33629), [#​33724](facebook/react#33724), [#​32735](facebook/react#32735), [#​33723](facebook/react#33723)) - Add `cacheSignal` ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33557](facebook/react#33557)) ##### React DOM - Block on Suspensey Fonts during reveal of server-side-rendered content ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33342](facebook/react#33342)) - Use underscore instead of `:` for IDs generated by `useId` ([@​sebmarkbage](https://github.com/sebmarkbage), [@​eps1lon](https://github.com/eps1lon): [#​32001](facebook/react#32001), [#​33342](https://github.com/facebook/react/pull/33342)[#​33099](https://github.com/facebook/react/pull/33099), [#​33422](facebook/react#33422)) - Stop warning when ARIA 1.3 attributes are used ([@​Abdul-Omira](https://github.com/Abdul-Omira) [#​34264](facebook/react#34264)) - Allow `nonce` to be used on hoistable styles ([@​Andarist](https://github.com/Andarist) [#​32461](facebook/react#32461)) - Warn for using a React owned node as a Container if it also has text content ([@​sebmarkbage](https://github.com/sebmarkbage) [#​32774](facebook/react#32774)) - s/HTML/text for for error messages if text hydration mismatches ([@​rickhanlonii](https://github.com/rickhanlonii) [#​32763](facebook/react#32763)) - Fix a bug with `React.use` inside `React.lazy`-ed Component ([@​hi-ogawa](https://github.com/hi-ogawa) [#​33941](facebook/react#33941)) - Enable the `progressiveChunkSize` option for server-side-rendering APIs ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33027](facebook/react#33027)) - Fix a bug with deeply nested Suspense inside Suspense fallback when server-side-rendering ([@​gnoff](https://github.com/gnoff) [#​33467](facebook/react#33467)) - Avoid hanging when suspending after aborting while rendering ([@​gnoff](https://github.com/gnoff) [#​34192](facebook/react#34192)) - Add Node Web Streams to server-side-rendering APIs for Node.js ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33475](facebook/react#33475)) ##### React Server Components - Preload `<img>` and `<link>` using hints before they're rendered ([@​sebmarkbage](https://github.com/sebmarkbage) [#​34604](facebook/react#34604)) - Log error if production elements are rendered during development ([@​eps1lon](https://github.com/eps1lon) [#​34189](facebook/react#34189)) - Fix a bug when returning a Temporary reference (e.g. a Client Reference) from Server Functions ([@​sebmarkbage](https://github.com/sebmarkbage) [#​34084](facebook/react#34084), [@​denk0403](https://github.com/denk0403) [#​33761](facebook/react#33761)) - Pass line/column to `filterStackFrame` ([@​eps1lon](https://github.com/eps1lon) [#​33707](facebook/react#33707)) - Support Async Modules in Turbopack Server References ([@​lubieowoce](https://github.com/lubieowoce) [#​34531](facebook/react#34531)) - Add support for .mjs file extension in Webpack ([@​jennyscript](https://github.com/jennyscript) [#​33028](facebook/react#33028)) - Fix a wrong missing key warning ([@​unstubbable](https://github.com/unstubbable) [#​34350](facebook/react#34350)) - Make console log resolve in predictable order ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33665](facebook/react#33665)) ##### React Reconciler - [createContainer](https://github.com/facebook/react/blob/v19.2.0/packages/react-reconciler/src/ReactFiberReconciler.js#L255-L261) and [createHydrationContainer](https://github.com/facebook/react/blob/v19.2.0/packages/react-reconciler/src/ReactFiberReconciler.js#L305-L312) had their parameter order adjusted after `on*` handlers to account for upcoming experimental APIs </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNDguNCIsInVwZGF0ZWRJblZlciI6IjQyLjEwLjUiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=--> Reviewed-on: https://git.in.csmpro.ru/csmpro/csm-mapban/pulls/36 Co-authored-by: Renovate Bot <renovate@csmpro.ru> Co-committed-by: Renovate Bot <renovate@csmpro.ru>
Fixes a bug in useDeferredValue's optional
initialValueargument. In the regression case, if a new useDeferredValue hook is mounted while an earlier transition is suspended, theinitialValueargument of the new hook was ignored. After the fix, theinitialValueargument is correctly rendered during the initial mount, regardless of whether other transitions were suspended.The culprit was related to the mechanism we use to track whether a render is the result of a
useDeferredValuehook: we assign the deferred lane a TransitionLane, then entangle that lane with the DeferredLane bit. During the subsequent render, we check for the presence of the DeferredLane bit to determine whether to switch to the final, canonical value.But because transition lanes can themselves become entangled with other transitions, the effect is that every entangled transition was being treated as if it were the result of a
useDeferredValuehook, causing us to skip the initial value and go straight to the final one.The fix I've chosen is to reserve some subset of TransitionLanes to be used only for deferred work, instead of using entanglement. This is similar to how retries are already implemented. Originally I tried not to implement it this way because it means there are now slightly fewer lanes allocated for regular transitions, but I underestimated how similar deferred work is to retries; they end up having a lot of the same requirements. Eventually it may be possible to merge the two concepts.