From 4374cb693d1572509aab1800d2a433b379625212 Mon Sep 17 00:00:00 2001 From: acdlite Date: Thu, 23 May 2024 21:24:58 +0000 Subject: [PATCH] Fix async batching in React.startTransition (#29226) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note: Despite the similar-sounding description, this fix is unrelated to the issue where updates that occur after an `await` in an async action must also be wrapped in their own `startTransition`, due to the absence of an AsyncContext mechanism in browsers today. --- Discovered a flaw in the current implementation of the isomorphic startTransition implementation (React.startTransition), related to async actions. It only creates an async scope if something calls setState within the synchronous part of the action (i.e. before the first `await`). I had thought this was fine because if there's no update during this part, then there's nothing that needs to be entangled. I didn't think this through, though — if there are multiple async updates interleaved throughout the rest of the action, we need the async scope to have already been created so that _those_ are batched together. An even easier way to observe this is to schedule an optimistic update after an `await` — the optimistic update should not be reverted until the async action is complete. To implement, during the reconciler's module initialization, we compose its startTransition implementation with any previous reconciler's startTransition that was already initialized. Then, the isomorphic startTransition is the composition of every reconciler's startTransition. ```js function startTransition(fn) { return startTransitionDOM(() => { return startTransitionART(() => { return startTransitionThreeFiber(() => { // and so on... return fn(); }); }); }); } ``` This is basically how flushSync is implemented, too. DiffTrain build for [ee5c19493086fdeb32057e16d1e3414370242307](https://github.com/facebook/react/commit/ee5c19493086fdeb32057e16d1e3414370242307) --- compiled/facebook-www/REVISION | 2 +- compiled/facebook-www/React-dev.classic.js | 22 +++-- compiled/facebook-www/React-dev.modern.js | 22 +++-- compiled/facebook-www/React-prod.classic.js | 19 +++-- compiled/facebook-www/React-prod.modern.js | 19 +++-- .../facebook-www/React-profiling.classic.js | 19 +++-- .../facebook-www/React-profiling.modern.js | 19 +++-- compiled/facebook-www/ReactART-dev.classic.js | 81 +++++++++++-------- compiled/facebook-www/ReactART-dev.modern.js | 81 +++++++++++-------- .../facebook-www/ReactART-prod.classic.js | 47 ++++++----- compiled/facebook-www/ReactART-prod.modern.js | 47 ++++++----- compiled/facebook-www/ReactDOM-dev.classic.js | 81 +++++++++++-------- compiled/facebook-www/ReactDOM-dev.modern.js | 81 +++++++++++-------- .../facebook-www/ReactDOM-prod.classic.js | 50 ++++++------ compiled/facebook-www/ReactDOM-prod.modern.js | 50 ++++++------ .../ReactDOM-profiling.classic.js | 50 ++++++------ .../facebook-www/ReactDOM-profiling.modern.js | 50 ++++++------ .../ReactDOMTesting-dev.classic.js | 81 +++++++++++-------- .../ReactDOMTesting-dev.modern.js | 81 +++++++++++-------- .../ReactDOMTesting-prod.classic.js | 50 ++++++------ .../ReactDOMTesting-prod.modern.js | 50 ++++++------ .../ReactReconciler-dev.classic.js | 81 +++++++++++-------- .../ReactReconciler-dev.modern.js | 81 +++++++++++-------- .../ReactReconciler-prod.classic.js | 48 ++++++----- .../ReactReconciler-prod.modern.js | 48 ++++++----- .../ReactTestRenderer-dev.classic.js | 81 +++++++++++-------- .../ReactTestRenderer-dev.modern.js | 81 +++++++++++-------- 27 files changed, 783 insertions(+), 639 deletions(-) diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index ac73a4bcae47d..e451595645d89 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -f55d172bcf921d761733533395b798c5b3665e04 +ee5c19493086fdeb32057e16d1e3414370242307 diff --git a/compiled/facebook-www/React-dev.classic.js b/compiled/facebook-www/React-dev.classic.js index 02dbec55e08db..a899471090951 100644 --- a/compiled/facebook-www/React-dev.classic.js +++ b/compiled/facebook-www/React-dev.classic.js @@ -22,7 +22,7 @@ if ( ) { __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error()); } -var ReactVersion = '19.0.0-www-classic-87c03497'; +var ReactVersion = '19.0.0-www-classic-f3d67148'; // Re-export dynamic flags from the www version. var dynamicFeatureFlags = require('ReactFeatureFlags'); @@ -564,7 +564,8 @@ function getComponentNameFromType(type) { var ReactSharedInternals = { H: null, A: null, - T: null + T: null, + S: null }; { @@ -2956,13 +2957,8 @@ reportError : function (error) { }; function startTransition(scope, options) { - var prevTransition = ReactSharedInternals.T; // Each renderer registers a callback to receive the return value of - // the scope function. This is used to implement async actions. - - var callbacks = new Set(); - var transition = { - _callbacks: callbacks - }; + var prevTransition = ReactSharedInternals.T; + var transition = {}; ReactSharedInternals.T = transition; var currentTransition = ReactSharedInternals.T; @@ -2982,11 +2978,13 @@ function startTransition(scope, options) { { try { var returnValue = scope(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(transition, returnValue); + } if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { - callbacks.forEach(function (callback) { - return callback(currentTransition, returnValue); - }); returnValue.then(noop, reportGlobalError); } } catch (error) { diff --git a/compiled/facebook-www/React-dev.modern.js b/compiled/facebook-www/React-dev.modern.js index 499140807952b..afa63ffba4792 100644 --- a/compiled/facebook-www/React-dev.modern.js +++ b/compiled/facebook-www/React-dev.modern.js @@ -22,7 +22,7 @@ if ( ) { __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error()); } -var ReactVersion = '19.0.0-www-modern-005a649e'; +var ReactVersion = '19.0.0-www-modern-7638663c'; // Re-export dynamic flags from the www version. var dynamicFeatureFlags = require('ReactFeatureFlags'); @@ -564,7 +564,8 @@ function getComponentNameFromType(type) { var ReactSharedInternals = { H: null, A: null, - T: null + T: null, + S: null }; { @@ -2959,13 +2960,8 @@ reportError : function (error) { }; function startTransition(scope, options) { - var prevTransition = ReactSharedInternals.T; // Each renderer registers a callback to receive the return value of - // the scope function. This is used to implement async actions. - - var callbacks = new Set(); - var transition = { - _callbacks: callbacks - }; + var prevTransition = ReactSharedInternals.T; + var transition = {}; ReactSharedInternals.T = transition; var currentTransition = ReactSharedInternals.T; @@ -2985,11 +2981,13 @@ function startTransition(scope, options) { { try { var returnValue = scope(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(transition, returnValue); + } if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { - callbacks.forEach(function (callback) { - return callback(currentTransition, returnValue); - }); returnValue.then(noop, reportGlobalError); } } catch (error) { diff --git a/compiled/facebook-www/React-prod.classic.js b/compiled/facebook-www/React-prod.classic.js index 4d0e2dc0d94f4..0b7d203a39dad 100644 --- a/compiled/facebook-www/React-prod.classic.js +++ b/compiled/facebook-www/React-prod.classic.js @@ -88,7 +88,7 @@ pureComponentPrototype.constructor = PureComponent; assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = !0; var isArrayImpl = Array.isArray, - ReactSharedInternals = { H: null, A: null, T: null }, + ReactSharedInternals = { H: null, A: null, T: null, S: null }, hasOwnProperty = Object.prototype.hasOwnProperty; function getOwner() { var dispatcher = ReactSharedInternals.A; @@ -588,23 +588,22 @@ exports.memo = function (type, compare) { }; exports.startTransition = function (scope, options) { var prevTransition = ReactSharedInternals.T, - callbacks = new Set(); - ReactSharedInternals.T = { _callbacks: callbacks }; - var currentTransition = ReactSharedInternals.T; + transition = {}; + ReactSharedInternals.T = transition; enableTransitionTracing && void 0 !== options && void 0 !== options.name && ((ReactSharedInternals.T.name = options.name), (ReactSharedInternals.T.startTime = -1)); try { - var returnValue = scope(); + var returnValue = scope(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(transition, returnValue); "object" === typeof returnValue && null !== returnValue && "function" === typeof returnValue.then && - (callbacks.forEach(function (callback) { - return callback(currentTransition, returnValue); - }), - returnValue.then(noop, reportGlobalError)); + returnValue.then(noop, reportGlobalError); } catch (error) { reportGlobalError(error); } finally { @@ -685,4 +684,4 @@ exports.useSyncExternalStore = function ( exports.useTransition = function () { return ReactSharedInternals.H.useTransition(); }; -exports.version = "19.0.0-www-classic-e55b1679"; +exports.version = "19.0.0-www-classic-abbf2e79"; diff --git a/compiled/facebook-www/React-prod.modern.js b/compiled/facebook-www/React-prod.modern.js index 8ec56819b300a..be6019a1e276a 100644 --- a/compiled/facebook-www/React-prod.modern.js +++ b/compiled/facebook-www/React-prod.modern.js @@ -88,7 +88,7 @@ pureComponentPrototype.constructor = PureComponent; assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = !0; var isArrayImpl = Array.isArray, - ReactSharedInternals = { H: null, A: null, T: null }, + ReactSharedInternals = { H: null, A: null, T: null, S: null }, hasOwnProperty = Object.prototype.hasOwnProperty; function getOwner() { var dispatcher = ReactSharedInternals.A; @@ -588,23 +588,22 @@ exports.memo = function (type, compare) { }; exports.startTransition = function (scope, options) { var prevTransition = ReactSharedInternals.T, - callbacks = new Set(); - ReactSharedInternals.T = { _callbacks: callbacks }; - var currentTransition = ReactSharedInternals.T; + transition = {}; + ReactSharedInternals.T = transition; enableTransitionTracing && void 0 !== options && void 0 !== options.name && ((ReactSharedInternals.T.name = options.name), (ReactSharedInternals.T.startTime = -1)); try { - var returnValue = scope(); + var returnValue = scope(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(transition, returnValue); "object" === typeof returnValue && null !== returnValue && "function" === typeof returnValue.then && - (callbacks.forEach(function (callback) { - return callback(currentTransition, returnValue); - }), - returnValue.then(noop, reportGlobalError)); + returnValue.then(noop, reportGlobalError); } catch (error) { reportGlobalError(error); } finally { @@ -685,4 +684,4 @@ exports.useSyncExternalStore = function ( exports.useTransition = function () { return ReactSharedInternals.H.useTransition(); }; -exports.version = "19.0.0-www-modern-e55b1679"; +exports.version = "19.0.0-www-modern-abbf2e79"; diff --git a/compiled/facebook-www/React-profiling.classic.js b/compiled/facebook-www/React-profiling.classic.js index 13e5fe971c6eb..b67823963a22d 100644 --- a/compiled/facebook-www/React-profiling.classic.js +++ b/compiled/facebook-www/React-profiling.classic.js @@ -92,7 +92,7 @@ pureComponentPrototype.constructor = PureComponent; assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = !0; var isArrayImpl = Array.isArray, - ReactSharedInternals = { H: null, A: null, T: null }, + ReactSharedInternals = { H: null, A: null, T: null, S: null }, hasOwnProperty = Object.prototype.hasOwnProperty; function getOwner() { var dispatcher = ReactSharedInternals.A; @@ -592,23 +592,22 @@ exports.memo = function (type, compare) { }; exports.startTransition = function (scope, options) { var prevTransition = ReactSharedInternals.T, - callbacks = new Set(); - ReactSharedInternals.T = { _callbacks: callbacks }; - var currentTransition = ReactSharedInternals.T; + transition = {}; + ReactSharedInternals.T = transition; enableTransitionTracing && void 0 !== options && void 0 !== options.name && ((ReactSharedInternals.T.name = options.name), (ReactSharedInternals.T.startTime = -1)); try { - var returnValue = scope(); + var returnValue = scope(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(transition, returnValue); "object" === typeof returnValue && null !== returnValue && "function" === typeof returnValue.then && - (callbacks.forEach(function (callback) { - return callback(currentTransition, returnValue); - }), - returnValue.then(noop, reportGlobalError)); + returnValue.then(noop, reportGlobalError); } catch (error) { reportGlobalError(error); } finally { @@ -689,7 +688,7 @@ exports.useSyncExternalStore = function ( exports.useTransition = function () { return ReactSharedInternals.H.useTransition(); }; -exports.version = "19.0.0-www-classic-3337290d"; +exports.version = "19.0.0-www-classic-d6ae41f2"; "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && diff --git a/compiled/facebook-www/React-profiling.modern.js b/compiled/facebook-www/React-profiling.modern.js index 638049e79dfb2..4f1b0dcc4694e 100644 --- a/compiled/facebook-www/React-profiling.modern.js +++ b/compiled/facebook-www/React-profiling.modern.js @@ -92,7 +92,7 @@ pureComponentPrototype.constructor = PureComponent; assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = !0; var isArrayImpl = Array.isArray, - ReactSharedInternals = { H: null, A: null, T: null }, + ReactSharedInternals = { H: null, A: null, T: null, S: null }, hasOwnProperty = Object.prototype.hasOwnProperty; function getOwner() { var dispatcher = ReactSharedInternals.A; @@ -592,23 +592,22 @@ exports.memo = function (type, compare) { }; exports.startTransition = function (scope, options) { var prevTransition = ReactSharedInternals.T, - callbacks = new Set(); - ReactSharedInternals.T = { _callbacks: callbacks }; - var currentTransition = ReactSharedInternals.T; + transition = {}; + ReactSharedInternals.T = transition; enableTransitionTracing && void 0 !== options && void 0 !== options.name && ((ReactSharedInternals.T.name = options.name), (ReactSharedInternals.T.startTime = -1)); try { - var returnValue = scope(); + var returnValue = scope(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(transition, returnValue); "object" === typeof returnValue && null !== returnValue && "function" === typeof returnValue.then && - (callbacks.forEach(function (callback) { - return callback(currentTransition, returnValue); - }), - returnValue.then(noop, reportGlobalError)); + returnValue.then(noop, reportGlobalError); } catch (error) { reportGlobalError(error); } finally { @@ -689,7 +688,7 @@ exports.useSyncExternalStore = function ( exports.useTransition = function () { return ReactSharedInternals.H.useTransition(); }; -exports.version = "19.0.0-www-modern-3337290d"; +exports.version = "19.0.0-www-modern-d6ae41f2"; "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && diff --git a/compiled/facebook-www/ReactART-dev.classic.js b/compiled/facebook-www/ReactART-dev.classic.js index 1a947000b7eb5..43dbb11760e3d 100644 --- a/compiled/facebook-www/ReactART-dev.classic.js +++ b/compiled/facebook-www/ReactART-dev.classic.js @@ -60,7 +60,7 @@ function _assertThisInitialized(self) { return self; } -var ReactVersion = '19.0.0-www-classic-df6992f9'; +var ReactVersion = '19.0.0-www-classic-1adb5d06'; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -9243,9 +9243,7 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var prevState = actionQueue.state; // This is a fork of startTransition var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; ReactSharedInternals.T = currentTransition; { @@ -9258,11 +9256,15 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { try { var returnValue = action(prevState, payload); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } if (returnValue !== null && typeof returnValue === 'object' && // $FlowFixMe[method-unbinding] typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Attach a listener to read the return state of the action. As soon as + var thenable = returnValue; // Attach a listener to read the return state of the action. As soon as // this resolves, we can run the next action in the sequence. thenable.then(function (nextState) { @@ -9832,9 +9834,7 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op var previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; { // We don't really need to use an optimistic update here, because we @@ -9860,7 +9860,12 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op try { if (enableAsyncActions) { - var returnValue = callback(); // Check if we're inside an async action scope. If so, we'll entangle + var returnValue = callback(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } // Check if we're inside an async action scope. If so, we'll entangle // this new action with the existing scope. // // If we're not already inside an async action scope, and this action is @@ -9869,9 +9874,9 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op // In the async case, the resulting render will suspend until the async // action scope has finished. + if (returnValue !== null && typeof returnValue === 'object' && typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Create a thenable that resolves to `finishedState` once the async + var thenable = returnValue; // Create a thenable that resolves to `finishedState` once the async // action has completed. var thenableForFinishedState = chainThenableValue(thenable, finishedState); @@ -16725,30 +16730,42 @@ function popCacheProvider(workInProgress, cache) { popProvider(CacheContext, workInProgress); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - - if (transition !== null) { - // Whenever a transition update is scheduled, register a callback on the - // transition object so we can get the return value of the scope function. - transition._callbacks.add(handleAsyncAction); +// the shared internals object. This is used by the isomorphic implementation of +// startTransition to compose all the startTransitions together. +// +// function startTransition(fn) { +// return startTransitionDOM(() => { +// return startTransitionART(() => { +// return startTransitionThreeFiber(() => { +// // and so on... +// return fn(); +// }); +// }); +// }); +// } +// +// Currently we only compose together the code that runs at the end of each +// startTransition, because for now that's sufficient — the part that sets +// isTransition=true on the stack uses a separate shared internal field. But +// really we should delete the shared field and track isTransition per +// reconciler. Leaving this for a future PR. + +var prevOnStartTransitionFinish = ReactSharedInternals.S; + +ReactSharedInternals.S = function onStartTransitionFinishForReconciler(transition, returnValue) { + if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { + // This is an async action + var thenable = returnValue; + entangleAsyncAction(transition, thenable); } - return transition; -} - -function handleAsyncAction(transition, thenable) { - { - // This is an async action. - entangleAsyncAction(transition, thenable); + if (prevOnStartTransitionFinish !== null) { + prevOnStartTransitionFinish(transition, returnValue); } -} +}; -function notifyTransitionCallbacks(transition, returnValue) { - var callbacks = transition._callbacks; - callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); +function requestCurrentTransition() { + return ReactSharedInternals.T; } // When retrying a Suspense/Offscreen boundary, we restore the cache that was // used during the previous render by placing it here, on the stack. diff --git a/compiled/facebook-www/ReactART-dev.modern.js b/compiled/facebook-www/ReactART-dev.modern.js index f51f545d314bb..e011f600601ed 100644 --- a/compiled/facebook-www/ReactART-dev.modern.js +++ b/compiled/facebook-www/ReactART-dev.modern.js @@ -60,7 +60,7 @@ function _assertThisInitialized(self) { return self; } -var ReactVersion = '19.0.0-www-modern-e0053f93'; +var ReactVersion = '19.0.0-www-modern-601320d5'; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -9032,9 +9032,7 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var prevState = actionQueue.state; // This is a fork of startTransition var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; ReactSharedInternals.T = currentTransition; { @@ -9047,11 +9045,15 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { try { var returnValue = action(prevState, payload); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } if (returnValue !== null && typeof returnValue === 'object' && // $FlowFixMe[method-unbinding] typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Attach a listener to read the return state of the action. As soon as + var thenable = returnValue; // Attach a listener to read the return state of the action. As soon as // this resolves, we can run the next action in the sequence. thenable.then(function (nextState) { @@ -9621,9 +9623,7 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op var previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; { // We don't really need to use an optimistic update here, because we @@ -9649,7 +9649,12 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op try { if (enableAsyncActions) { - var returnValue = callback(); // Check if we're inside an async action scope. If so, we'll entangle + var returnValue = callback(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } // Check if we're inside an async action scope. If so, we'll entangle // this new action with the existing scope. // // If we're not already inside an async action scope, and this action is @@ -9658,9 +9663,9 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op // In the async case, the resulting render will suspend until the async // action scope has finished. + if (returnValue !== null && typeof returnValue === 'object' && typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Create a thenable that resolves to `finishedState` once the async + var thenable = returnValue; // Create a thenable that resolves to `finishedState` once the async // action has completed. var thenableForFinishedState = chainThenableValue(thenable, finishedState); @@ -16250,30 +16255,42 @@ function popCacheProvider(workInProgress, cache) { popProvider(CacheContext, workInProgress); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - - if (transition !== null) { - // Whenever a transition update is scheduled, register a callback on the - // transition object so we can get the return value of the scope function. - transition._callbacks.add(handleAsyncAction); +// the shared internals object. This is used by the isomorphic implementation of +// startTransition to compose all the startTransitions together. +// +// function startTransition(fn) { +// return startTransitionDOM(() => { +// return startTransitionART(() => { +// return startTransitionThreeFiber(() => { +// // and so on... +// return fn(); +// }); +// }); +// }); +// } +// +// Currently we only compose together the code that runs at the end of each +// startTransition, because for now that's sufficient — the part that sets +// isTransition=true on the stack uses a separate shared internal field. But +// really we should delete the shared field and track isTransition per +// reconciler. Leaving this for a future PR. + +var prevOnStartTransitionFinish = ReactSharedInternals.S; + +ReactSharedInternals.S = function onStartTransitionFinishForReconciler(transition, returnValue) { + if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { + // This is an async action + var thenable = returnValue; + entangleAsyncAction(transition, thenable); } - return transition; -} - -function handleAsyncAction(transition, thenable) { - { - // This is an async action. - entangleAsyncAction(transition, thenable); + if (prevOnStartTransitionFinish !== null) { + prevOnStartTransitionFinish(transition, returnValue); } -} +}; -function notifyTransitionCallbacks(transition, returnValue) { - var callbacks = transition._callbacks; - callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); +function requestCurrentTransition() { + return ReactSharedInternals.T; } // When retrying a Suspense/Offscreen boundary, we restore the cache that was // used during the previous render by placing it here, on the stack. diff --git a/compiled/facebook-www/ReactART-prod.classic.js b/compiled/facebook-www/ReactART-prod.classic.js index ea555884f0b41..89c2eb8ae7107 100644 --- a/compiled/facebook-www/ReactART-prod.classic.js +++ b/compiled/facebook-www/ReactART-prod.classic.js @@ -3022,16 +3022,18 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var action = actionQueue.action, prevState = actionQueue.state, prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; setPendingState(!0); try { - var returnValue = action(prevState, payload); + var returnValue = action(prevState, payload), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then - ? (notifyTransitionCallbacks(currentTransition, returnValue), - returnValue.then( + ? (returnValue.then( function (nextState) { actionQueue.state = nextState; finishRunningActionStateAction( @@ -3327,7 +3329,7 @@ function startTransition( currentUpdatePriority = 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8; var prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; dispatchOptimisticSetState(fiber, !1, queue, pendingState); enableTransitionTracing && @@ -3336,13 +3338,15 @@ function startTransition( ((currentTransition.name = options.name), (currentTransition.startTime = now())); try { - var returnValue = callback(); + var returnValue = callback(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); if ( null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then ) { - notifyTransitionCallbacks(currentTransition, returnValue); var thenableForFinishedState = chainThenableValue( returnValue, finishedState @@ -3447,7 +3451,6 @@ function dispatchSetState(fiber, queue, action) { } } function dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) { - requestCurrentTransition(); action = { lane: 2, revertLane: requestTransitionLane(), @@ -6366,19 +6369,15 @@ function releaseCache(cache) { cache.controller.abort(); }); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - null !== transition && transition._callbacks.add(handleAsyncAction); - return transition; -} -function handleAsyncAction(transition, thenable) { - entangleAsyncAction(transition, thenable); -} -function notifyTransitionCallbacks(transition, returnValue) { - transition._callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); -} +var prevOnStartTransitionFinish = ReactSharedInternals.S; +ReactSharedInternals.S = function (transition, returnValue) { + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + entangleAsyncAction(transition, returnValue); + null !== prevOnStartTransitionFinish && + prevOnStartTransitionFinish(transition, returnValue); +}; var resumedCache = createCursor(null), transitionStack = createCursor(null); function peekCacheFromPool() { @@ -9035,7 +9034,7 @@ function requestUpdateLane(fiber) { ? 2 : 0 !== (executionContext & 2) && 0 !== workInProgressRootRenderLanes ? workInProgressRootRenderLanes & -workInProgressRootRenderLanes - : null !== requestCurrentTransition() + : null !== ReactSharedInternals.T ? ((fiber = currentEntangledLane), 0 !== fiber ? fiber : requestTransitionLane()) : currentUpdatePriority || 32; @@ -10649,7 +10648,7 @@ var slice = Array.prototype.slice, return null; }, bundleType: 0, - version: "19.0.0-www-classic-aee192a3", + version: "19.0.0-www-classic-3166a256", rendererPackageName: "react-art" }; var internals$jscomp$inline_1358 = { @@ -10680,7 +10679,7 @@ var internals$jscomp$inline_1358 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "19.0.0-www-classic-aee192a3" + reconcilerVersion: "19.0.0-www-classic-3166a256" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1359 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled/facebook-www/ReactART-prod.modern.js b/compiled/facebook-www/ReactART-prod.modern.js index fd9418a3f53cd..45f38925f5d2c 100644 --- a/compiled/facebook-www/ReactART-prod.modern.js +++ b/compiled/facebook-www/ReactART-prod.modern.js @@ -2820,16 +2820,18 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var action = actionQueue.action, prevState = actionQueue.state, prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; setPendingState(!0); try { - var returnValue = action(prevState, payload); + var returnValue = action(prevState, payload), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then - ? (notifyTransitionCallbacks(currentTransition, returnValue), - returnValue.then( + ? (returnValue.then( function (nextState) { actionQueue.state = nextState; finishRunningActionStateAction( @@ -3125,7 +3127,7 @@ function startTransition( currentUpdatePriority = 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8; var prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; dispatchOptimisticSetState(fiber, !1, queue, pendingState); enableTransitionTracing && @@ -3134,13 +3136,15 @@ function startTransition( ((currentTransition.name = options.name), (currentTransition.startTime = now())); try { - var returnValue = callback(); + var returnValue = callback(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); if ( null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then ) { - notifyTransitionCallbacks(currentTransition, returnValue); var thenableForFinishedState = chainThenableValue( returnValue, finishedState @@ -3245,7 +3249,6 @@ function dispatchSetState(fiber, queue, action) { } } function dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) { - requestCurrentTransition(); action = { lane: 2, revertLane: requestTransitionLane(), @@ -5950,19 +5953,15 @@ function releaseCache(cache) { cache.controller.abort(); }); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - null !== transition && transition._callbacks.add(handleAsyncAction); - return transition; -} -function handleAsyncAction(transition, thenable) { - entangleAsyncAction(transition, thenable); -} -function notifyTransitionCallbacks(transition, returnValue) { - transition._callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); -} +var prevOnStartTransitionFinish = ReactSharedInternals.S; +ReactSharedInternals.S = function (transition, returnValue) { + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + entangleAsyncAction(transition, returnValue); + null !== prevOnStartTransitionFinish && + prevOnStartTransitionFinish(transition, returnValue); +}; var resumedCache = createCursor(null), transitionStack = createCursor(null); function peekCacheFromPool() { @@ -8564,7 +8563,7 @@ var legacyErrorBoundariesThatAlreadyFailed = null, function requestUpdateLane() { if (0 !== (executionContext & 2) && 0 !== workInProgressRootRenderLanes) return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; - if (null !== requestCurrentTransition()) { + if (null !== ReactSharedInternals.T) { var actionScopeLane = currentEntangledLane; return 0 !== actionScopeLane ? actionScopeLane : requestTransitionLane(); } @@ -10124,7 +10123,7 @@ var slice = Array.prototype.slice, return null; }, bundleType: 0, - version: "19.0.0-www-modern-4c2dd93a", + version: "19.0.0-www-modern-11d27244", rendererPackageName: "react-art" }; var internals$jscomp$inline_1345 = { @@ -10155,7 +10154,7 @@ var internals$jscomp$inline_1345 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "19.0.0-www-modern-4c2dd93a" + reconcilerVersion: "19.0.0-www-modern-11d27244" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1346 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled/facebook-www/ReactDOM-dev.classic.js b/compiled/facebook-www/ReactDOM-dev.classic.js index dd4965372bc80..3e788be68dc39 100644 --- a/compiled/facebook-www/ReactDOM-dev.classic.js +++ b/compiled/facebook-www/ReactDOM-dev.classic.js @@ -12913,9 +12913,7 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var prevState = actionQueue.state; // This is a fork of startTransition var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; ReactSharedInternals.T = currentTransition; { @@ -12928,11 +12926,15 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { try { var returnValue = action(prevState, payload); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } if (returnValue !== null && typeof returnValue === 'object' && // $FlowFixMe[method-unbinding] typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Attach a listener to read the return state of the action. As soon as + var thenable = returnValue; // Attach a listener to read the return state of the action. As soon as // this resolves, we can run the next action in the sequence. thenable.then(function (nextState) { @@ -13517,9 +13519,7 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op var previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; { // We don't really need to use an optimistic update here, because we @@ -13545,7 +13545,12 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op try { if (enableAsyncActions) { - var returnValue = callback(); // Check if we're inside an async action scope. If so, we'll entangle + var returnValue = callback(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } // Check if we're inside an async action scope. If so, we'll entangle // this new action with the existing scope. // // If we're not already inside an async action scope, and this action is @@ -13554,9 +13559,9 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op // In the async case, the resulting render will suspend until the async // action scope has finished. + if (returnValue !== null && typeof returnValue === 'object' && typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Create a thenable that resolves to `finishedState` once the async + var thenable = returnValue; // Create a thenable that resolves to `finishedState` once the async // action has completed. var thenableForFinishedState = chainThenableValue(thenable, finishedState); @@ -20842,30 +20847,42 @@ function popCacheProvider(workInProgress, cache) { popProvider(CacheContext, workInProgress); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - - if (transition !== null) { - // Whenever a transition update is scheduled, register a callback on the - // transition object so we can get the return value of the scope function. - transition._callbacks.add(handleAsyncAction); +// the shared internals object. This is used by the isomorphic implementation of +// startTransition to compose all the startTransitions together. +// +// function startTransition(fn) { +// return startTransitionDOM(() => { +// return startTransitionART(() => { +// return startTransitionThreeFiber(() => { +// // and so on... +// return fn(); +// }); +// }); +// }); +// } +// +// Currently we only compose together the code that runs at the end of each +// startTransition, because for now that's sufficient — the part that sets +// isTransition=true on the stack uses a separate shared internal field. But +// really we should delete the shared field and track isTransition per +// reconciler. Leaving this for a future PR. + +var prevOnStartTransitionFinish = ReactSharedInternals.S; + +ReactSharedInternals.S = function onStartTransitionFinishForReconciler(transition, returnValue) { + if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { + // This is an async action + var thenable = returnValue; + entangleAsyncAction(transition, thenable); } - return transition; -} - -function handleAsyncAction(transition, thenable) { - { - // This is an async action. - entangleAsyncAction(transition, thenable); + if (prevOnStartTransitionFinish !== null) { + prevOnStartTransitionFinish(transition, returnValue); } -} +}; -function notifyTransitionCallbacks(transition, returnValue) { - var callbacks = transition._callbacks; - callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); +function requestCurrentTransition() { + return ReactSharedInternals.T; } // When retrying a Suspense/Offscreen boundary, we restore the cache that was // used during the previous render by placing it here, on the stack. @@ -30999,7 +31016,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition return root; } -var ReactVersion = '19.0.0-www-classic-16ee3628'; +var ReactVersion = '19.0.0-www-classic-3f1de443'; function createPortal$1(children, containerInfo, // TODO: figure out the API for cross-renderer implementation. implementation) { diff --git a/compiled/facebook-www/ReactDOM-dev.modern.js b/compiled/facebook-www/ReactDOM-dev.modern.js index eba0948b17621..d243eb68906dd 100644 --- a/compiled/facebook-www/ReactDOM-dev.modern.js +++ b/compiled/facebook-www/ReactDOM-dev.modern.js @@ -12654,9 +12654,7 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var prevState = actionQueue.state; // This is a fork of startTransition var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; ReactSharedInternals.T = currentTransition; { @@ -12669,11 +12667,15 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { try { var returnValue = action(prevState, payload); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } if (returnValue !== null && typeof returnValue === 'object' && // $FlowFixMe[method-unbinding] typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Attach a listener to read the return state of the action. As soon as + var thenable = returnValue; // Attach a listener to read the return state of the action. As soon as // this resolves, we can run the next action in the sequence. thenable.then(function (nextState) { @@ -13258,9 +13260,7 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op var previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; { // We don't really need to use an optimistic update here, because we @@ -13286,7 +13286,12 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op try { if (enableAsyncActions) { - var returnValue = callback(); // Check if we're inside an async action scope. If so, we'll entangle + var returnValue = callback(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } // Check if we're inside an async action scope. If so, we'll entangle // this new action with the existing scope. // // If we're not already inside an async action scope, and this action is @@ -13295,9 +13300,9 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op // In the async case, the resulting render will suspend until the async // action scope has finished. + if (returnValue !== null && typeof returnValue === 'object' && typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Create a thenable that resolves to `finishedState` once the async + var thenable = returnValue; // Create a thenable that resolves to `finishedState` once the async // action has completed. var thenableForFinishedState = chainThenableValue(thenable, finishedState); @@ -20319,30 +20324,42 @@ function popCacheProvider(workInProgress, cache) { popProvider(CacheContext, workInProgress); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - - if (transition !== null) { - // Whenever a transition update is scheduled, register a callback on the - // transition object so we can get the return value of the scope function. - transition._callbacks.add(handleAsyncAction); +// the shared internals object. This is used by the isomorphic implementation of +// startTransition to compose all the startTransitions together. +// +// function startTransition(fn) { +// return startTransitionDOM(() => { +// return startTransitionART(() => { +// return startTransitionThreeFiber(() => { +// // and so on... +// return fn(); +// }); +// }); +// }); +// } +// +// Currently we only compose together the code that runs at the end of each +// startTransition, because for now that's sufficient — the part that sets +// isTransition=true on the stack uses a separate shared internal field. But +// really we should delete the shared field and track isTransition per +// reconciler. Leaving this for a future PR. + +var prevOnStartTransitionFinish = ReactSharedInternals.S; + +ReactSharedInternals.S = function onStartTransitionFinishForReconciler(transition, returnValue) { + if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { + // This is an async action + var thenable = returnValue; + entangleAsyncAction(transition, thenable); } - return transition; -} - -function handleAsyncAction(transition, thenable) { - { - // This is an async action. - entangleAsyncAction(transition, thenable); + if (prevOnStartTransitionFinish !== null) { + prevOnStartTransitionFinish(transition, returnValue); } -} +}; -function notifyTransitionCallbacks(transition, returnValue) { - var callbacks = transition._callbacks; - callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); +function requestCurrentTransition() { + return ReactSharedInternals.T; } // When retrying a Suspense/Offscreen boundary, we restore the cache that was // used during the previous render by placing it here, on the stack. @@ -30174,7 +30191,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition return root; } -var ReactVersion = '19.0.0-www-modern-f5ea7daf'; +var ReactVersion = '19.0.0-www-modern-871d8931'; function createPortal$1(children, containerInfo, // TODO: figure out the API for cross-renderer implementation. implementation) { diff --git a/compiled/facebook-www/ReactDOM-prod.classic.js b/compiled/facebook-www/ReactDOM-prod.classic.js index d368724162021..05271facc1ff1 100644 --- a/compiled/facebook-www/ReactDOM-prod.classic.js +++ b/compiled/facebook-www/ReactDOM-prod.classic.js @@ -3743,16 +3743,18 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var action = actionQueue.action, prevState = actionQueue.state, prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; setPendingState(!0); try { - var returnValue = action(prevState, payload); + var returnValue = action(prevState, payload), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then - ? (notifyTransitionCallbacks(currentTransition, returnValue), - returnValue.then( + ? (returnValue.then( function (nextState) { actionQueue.state = nextState; finishRunningActionStateAction( @@ -4096,7 +4098,7 @@ function startTransition( Internals.p = 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8; var prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; dispatchOptimisticSetState(fiber, !1, queue, pendingState); enableTransitionTracing && @@ -4105,13 +4107,15 @@ function startTransition( ((currentTransition.name = options.name), (currentTransition.startTime = now())); try { - var returnValue = callback(); + var returnValue = callback(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); if ( null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then ) { - notifyTransitionCallbacks(currentTransition, returnValue); var thenableForFinishedState = chainThenableValue( returnValue, finishedState @@ -4181,7 +4185,6 @@ function ensureFormComponentIsStateful(formFiber) { return existingStateHook; } function requestFormReset$1(formFiber) { - requestCurrentTransition(); var resetStateQueue = ensureFormComponentIsStateful(formFiber).next.queue; dispatchSetState(formFiber, resetStateQueue, {}); } @@ -4272,7 +4275,6 @@ function dispatchSetState(fiber, queue, action) { } } function dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) { - requestCurrentTransition(); action = { lane: 2, revertLane: requestTransitionLane(), @@ -7535,19 +7537,15 @@ function releaseCache(cache) { cache.controller.abort(); }); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - null !== transition && transition._callbacks.add(handleAsyncAction); - return transition; -} -function handleAsyncAction(transition, thenable) { - entangleAsyncAction(transition, thenable); -} -function notifyTransitionCallbacks(transition, returnValue) { - transition._callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); -} +var prevOnStartTransitionFinish = ReactSharedInternals.S; +ReactSharedInternals.S = function (transition, returnValue) { + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + entangleAsyncAction(transition, returnValue); + null !== prevOnStartTransitionFinish && + prevOnStartTransitionFinish(transition, returnValue); +}; var resumedCache = createCursor(null), transitionStack = createCursor(null); function peekCacheFromPool() { @@ -10844,7 +10842,7 @@ function requestUpdateLane(fiber) { ? 2 : 0 !== (executionContext & 2) && 0 !== workInProgressRootRenderLanes ? workInProgressRootRenderLanes & -workInProgressRootRenderLanes - : null !== requestCurrentTransition() + : null !== ReactSharedInternals.T ? ((fiber = currentEntangledLane), 0 !== fiber ? fiber : requestTransitionLane()) : resolveUpdatePriority(); @@ -17070,7 +17068,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1746 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "19.0.0-www-classic-a0e0535d", + version: "19.0.0-www-classic-7a9d737b", rendererPackageName: "react-dom" }; var internals$jscomp$inline_2190 = { @@ -17100,7 +17098,7 @@ var internals$jscomp$inline_2190 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "19.0.0-www-classic-a0e0535d" + reconcilerVersion: "19.0.0-www-classic-7a9d737b" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_2191 = __REACT_DEVTOOLS_GLOBAL_HOOK__; @@ -17604,4 +17602,4 @@ exports.useFormState = function (action, initialState, permalink) { exports.useFormStatus = function () { return ReactSharedInternals.H.useHostTransitionStatus(); }; -exports.version = "19.0.0-www-classic-a0e0535d"; +exports.version = "19.0.0-www-classic-7a9d737b"; diff --git a/compiled/facebook-www/ReactDOM-prod.modern.js b/compiled/facebook-www/ReactDOM-prod.modern.js index c6a604a2fbb89..232d859ec5366 100644 --- a/compiled/facebook-www/ReactDOM-prod.modern.js +++ b/compiled/facebook-www/ReactDOM-prod.modern.js @@ -3531,16 +3531,18 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var action = actionQueue.action, prevState = actionQueue.state, prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; setPendingState(!0); try { - var returnValue = action(prevState, payload); + var returnValue = action(prevState, payload), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then - ? (notifyTransitionCallbacks(currentTransition, returnValue), - returnValue.then( + ? (returnValue.then( function (nextState) { actionQueue.state = nextState; finishRunningActionStateAction( @@ -3884,7 +3886,7 @@ function startTransition( Internals.p = 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8; var prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; dispatchOptimisticSetState(fiber, !1, queue, pendingState); enableTransitionTracing && @@ -3893,13 +3895,15 @@ function startTransition( ((currentTransition.name = options.name), (currentTransition.startTime = now())); try { - var returnValue = callback(); + var returnValue = callback(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); if ( null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then ) { - notifyTransitionCallbacks(currentTransition, returnValue); var thenableForFinishedState = chainThenableValue( returnValue, finishedState @@ -3969,7 +3973,6 @@ function ensureFormComponentIsStateful(formFiber) { return existingStateHook; } function requestFormReset$1(formFiber) { - requestCurrentTransition(); var resetStateQueue = ensureFormComponentIsStateful(formFiber).next.queue; dispatchSetState(formFiber, resetStateQueue, {}); } @@ -4060,7 +4063,6 @@ function dispatchSetState(fiber, queue, action) { } } function dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) { - requestCurrentTransition(); action = { lane: 2, revertLane: requestTransitionLane(), @@ -7077,19 +7079,15 @@ function releaseCache(cache) { cache.controller.abort(); }); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - null !== transition && transition._callbacks.add(handleAsyncAction); - return transition; -} -function handleAsyncAction(transition, thenable) { - entangleAsyncAction(transition, thenable); -} -function notifyTransitionCallbacks(transition, returnValue) { - transition._callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); -} +var prevOnStartTransitionFinish = ReactSharedInternals.S; +ReactSharedInternals.S = function (transition, returnValue) { + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + entangleAsyncAction(transition, returnValue); + null !== prevOnStartTransitionFinish && + prevOnStartTransitionFinish(transition, returnValue); +}; var resumedCache = createCursor(null), transitionStack = createCursor(null); function peekCacheFromPool() { @@ -10344,7 +10342,7 @@ var legacyErrorBoundariesThatAlreadyFailed = null, function requestUpdateLane() { if (0 !== (executionContext & 2) && 0 !== workInProgressRootRenderLanes) return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; - if (null !== requestCurrentTransition()) { + if (null !== ReactSharedInternals.T) { var actionScopeLane = currentEntangledLane; return 0 !== actionScopeLane ? actionScopeLane : requestTransitionLane(); } @@ -16433,7 +16431,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1713 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "19.0.0-www-modern-33a180b8", + version: "19.0.0-www-modern-1bab062b", rendererPackageName: "react-dom" }; var internals$jscomp$inline_2182 = { @@ -16463,7 +16461,7 @@ var internals$jscomp$inline_2182 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "19.0.0-www-modern-33a180b8" + reconcilerVersion: "19.0.0-www-modern-1bab062b" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_2183 = __REACT_DEVTOOLS_GLOBAL_HOOK__; @@ -16837,4 +16835,4 @@ exports.useFormState = function (action, initialState, permalink) { exports.useFormStatus = function () { return ReactSharedInternals.H.useHostTransitionStatus(); }; -exports.version = "19.0.0-www-modern-33a180b8"; +exports.version = "19.0.0-www-modern-1bab062b"; diff --git a/compiled/facebook-www/ReactDOM-profiling.classic.js b/compiled/facebook-www/ReactDOM-profiling.classic.js index 87662ce68e5a3..814f1050d4cb0 100644 --- a/compiled/facebook-www/ReactDOM-profiling.classic.js +++ b/compiled/facebook-www/ReactDOM-profiling.classic.js @@ -3879,16 +3879,18 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var action = actionQueue.action, prevState = actionQueue.state, prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; setPendingState(!0); try { - var returnValue = action(prevState, payload); + var returnValue = action(prevState, payload), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then - ? (notifyTransitionCallbacks(currentTransition, returnValue), - returnValue.then( + ? (returnValue.then( function (nextState) { actionQueue.state = nextState; finishRunningActionStateAction( @@ -4232,7 +4234,7 @@ function startTransition( Internals.p = 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8; var prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; dispatchOptimisticSetState(fiber, !1, queue, pendingState); enableTransitionTracing && @@ -4241,13 +4243,15 @@ function startTransition( ((currentTransition.name = options.name), (currentTransition.startTime = now$1())); try { - var returnValue = callback(); + var returnValue = callback(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); if ( null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then ) { - notifyTransitionCallbacks(currentTransition, returnValue); var thenableForFinishedState = chainThenableValue( returnValue, finishedState @@ -4317,7 +4321,6 @@ function ensureFormComponentIsStateful(formFiber) { return existingStateHook; } function requestFormReset$1(formFiber) { - requestCurrentTransition(); var resetStateQueue = ensureFormComponentIsStateful(formFiber).next.queue; dispatchSetState(formFiber, resetStateQueue, {}); } @@ -4410,7 +4413,6 @@ function dispatchSetState(fiber, queue, action) { enableSchedulingProfiler && markStateUpdateScheduled(fiber, lane); } function dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) { - requestCurrentTransition(); action = { lane: 2, revertLane: requestTransitionLane(), @@ -7783,19 +7785,15 @@ function releaseCache(cache) { cache.controller.abort(); }); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - null !== transition && transition._callbacks.add(handleAsyncAction); - return transition; -} -function handleAsyncAction(transition, thenable) { - entangleAsyncAction(transition, thenable); -} -function notifyTransitionCallbacks(transition, returnValue) { - transition._callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); -} +var prevOnStartTransitionFinish = ReactSharedInternals.S; +ReactSharedInternals.S = function (transition, returnValue) { + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + entangleAsyncAction(transition, returnValue); + null !== prevOnStartTransitionFinish && + prevOnStartTransitionFinish(transition, returnValue); +}; var resumedCache = createCursor(null), transitionStack = createCursor(null); function peekCacheFromPool() { @@ -11435,7 +11433,7 @@ function requestUpdateLane(fiber) { ? 2 : 0 !== (executionContext & 2) && 0 !== workInProgressRootRenderLanes ? workInProgressRootRenderLanes & -workInProgressRootRenderLanes - : null !== requestCurrentTransition() + : null !== ReactSharedInternals.T ? ((fiber = currentEntangledLane), 0 !== fiber ? fiber : requestTransitionLane()) : resolveUpdatePriority(); @@ -17834,7 +17832,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1832 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "19.0.0-www-classic-643b8964", + version: "19.0.0-www-classic-89895fc5", rendererPackageName: "react-dom" }; (function (internals) { @@ -17878,7 +17876,7 @@ var devToolsConfig$jscomp$inline_1832 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "19.0.0-www-classic-643b8964" + reconcilerVersion: "19.0.0-www-classic-89895fc5" }); function ReactDOMRoot(internalRoot) { this._internalRoot = internalRoot; @@ -18369,7 +18367,7 @@ exports.useFormState = function (action, initialState, permalink) { exports.useFormStatus = function () { return ReactSharedInternals.H.useHostTransitionStatus(); }; -exports.version = "19.0.0-www-classic-643b8964"; +exports.version = "19.0.0-www-classic-89895fc5"; "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && diff --git a/compiled/facebook-www/ReactDOM-profiling.modern.js b/compiled/facebook-www/ReactDOM-profiling.modern.js index 815b8fd4fcd0a..a77209f99e705 100644 --- a/compiled/facebook-www/ReactDOM-profiling.modern.js +++ b/compiled/facebook-www/ReactDOM-profiling.modern.js @@ -3667,16 +3667,18 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var action = actionQueue.action, prevState = actionQueue.state, prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; setPendingState(!0); try { - var returnValue = action(prevState, payload); + var returnValue = action(prevState, payload), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then - ? (notifyTransitionCallbacks(currentTransition, returnValue), - returnValue.then( + ? (returnValue.then( function (nextState) { actionQueue.state = nextState; finishRunningActionStateAction( @@ -4020,7 +4022,7 @@ function startTransition( Internals.p = 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8; var prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; dispatchOptimisticSetState(fiber, !1, queue, pendingState); enableTransitionTracing && @@ -4029,13 +4031,15 @@ function startTransition( ((currentTransition.name = options.name), (currentTransition.startTime = now$1())); try { - var returnValue = callback(); + var returnValue = callback(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); if ( null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then ) { - notifyTransitionCallbacks(currentTransition, returnValue); var thenableForFinishedState = chainThenableValue( returnValue, finishedState @@ -4105,7 +4109,6 @@ function ensureFormComponentIsStateful(formFiber) { return existingStateHook; } function requestFormReset$1(formFiber) { - requestCurrentTransition(); var resetStateQueue = ensureFormComponentIsStateful(formFiber).next.queue; dispatchSetState(formFiber, resetStateQueue, {}); } @@ -4198,7 +4201,6 @@ function dispatchSetState(fiber, queue, action) { enableSchedulingProfiler && markStateUpdateScheduled(fiber, lane); } function dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) { - requestCurrentTransition(); action = { lane: 2, revertLane: requestTransitionLane(), @@ -7308,19 +7310,15 @@ function releaseCache(cache) { cache.controller.abort(); }); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - null !== transition && transition._callbacks.add(handleAsyncAction); - return transition; -} -function handleAsyncAction(transition, thenable) { - entangleAsyncAction(transition, thenable); -} -function notifyTransitionCallbacks(transition, returnValue) { - transition._callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); -} +var prevOnStartTransitionFinish = ReactSharedInternals.S; +ReactSharedInternals.S = function (transition, returnValue) { + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + entangleAsyncAction(transition, returnValue); + null !== prevOnStartTransitionFinish && + prevOnStartTransitionFinish(transition, returnValue); +}; var resumedCache = createCursor(null), transitionStack = createCursor(null); function peekCacheFromPool() { @@ -10918,7 +10916,7 @@ var legacyErrorBoundariesThatAlreadyFailed = null, function requestUpdateLane() { if (0 !== (executionContext & 2) && 0 !== workInProgressRootRenderLanes) return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; - if (null !== requestCurrentTransition()) { + if (null !== ReactSharedInternals.T) { var actionScopeLane = currentEntangledLane; return 0 !== actionScopeLane ? actionScopeLane : requestTransitionLane(); } @@ -17180,7 +17178,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1799 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "19.0.0-www-modern-261a4fd2", + version: "19.0.0-www-modern-86c3ea22", rendererPackageName: "react-dom" }; (function (internals) { @@ -17224,7 +17222,7 @@ var devToolsConfig$jscomp$inline_1799 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "19.0.0-www-modern-261a4fd2" + reconcilerVersion: "19.0.0-www-modern-86c3ea22" }); function ReactDOMRoot(internalRoot) { this._internalRoot = internalRoot; @@ -17585,7 +17583,7 @@ exports.useFormState = function (action, initialState, permalink) { exports.useFormStatus = function () { return ReactSharedInternals.H.useHostTransitionStatus(); }; -exports.version = "19.0.0-www-modern-261a4fd2"; +exports.version = "19.0.0-www-modern-86c3ea22"; "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && diff --git a/compiled/facebook-www/ReactDOMTesting-dev.classic.js b/compiled/facebook-www/ReactDOMTesting-dev.classic.js index df103a24e0135..33faed87d8a4c 100644 --- a/compiled/facebook-www/ReactDOMTesting-dev.classic.js +++ b/compiled/facebook-www/ReactDOMTesting-dev.classic.js @@ -13054,9 +13054,7 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var prevState = actionQueue.state; // This is a fork of startTransition var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; ReactSharedInternals.T = currentTransition; { @@ -13069,11 +13067,15 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { try { var returnValue = action(prevState, payload); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } if (returnValue !== null && typeof returnValue === 'object' && // $FlowFixMe[method-unbinding] typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Attach a listener to read the return state of the action. As soon as + var thenable = returnValue; // Attach a listener to read the return state of the action. As soon as // this resolves, we can run the next action in the sequence. thenable.then(function (nextState) { @@ -13658,9 +13660,7 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op var previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; { // We don't really need to use an optimistic update here, because we @@ -13686,7 +13686,12 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op try { if (enableAsyncActions) { - var returnValue = callback(); // Check if we're inside an async action scope. If so, we'll entangle + var returnValue = callback(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } // Check if we're inside an async action scope. If so, we'll entangle // this new action with the existing scope. // // If we're not already inside an async action scope, and this action is @@ -13695,9 +13700,9 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op // In the async case, the resulting render will suspend until the async // action scope has finished. + if (returnValue !== null && typeof returnValue === 'object' && typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Create a thenable that resolves to `finishedState` once the async + var thenable = returnValue; // Create a thenable that resolves to `finishedState` once the async // action has completed. var thenableForFinishedState = chainThenableValue(thenable, finishedState); @@ -20983,30 +20988,42 @@ function popCacheProvider(workInProgress, cache) { popProvider(CacheContext, workInProgress); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - - if (transition !== null) { - // Whenever a transition update is scheduled, register a callback on the - // transition object so we can get the return value of the scope function. - transition._callbacks.add(handleAsyncAction); +// the shared internals object. This is used by the isomorphic implementation of +// startTransition to compose all the startTransitions together. +// +// function startTransition(fn) { +// return startTransitionDOM(() => { +// return startTransitionART(() => { +// return startTransitionThreeFiber(() => { +// // and so on... +// return fn(); +// }); +// }); +// }); +// } +// +// Currently we only compose together the code that runs at the end of each +// startTransition, because for now that's sufficient — the part that sets +// isTransition=true on the stack uses a separate shared internal field. But +// really we should delete the shared field and track isTransition per +// reconciler. Leaving this for a future PR. + +var prevOnStartTransitionFinish = ReactSharedInternals.S; + +ReactSharedInternals.S = function onStartTransitionFinishForReconciler(transition, returnValue) { + if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { + // This is an async action + var thenable = returnValue; + entangleAsyncAction(transition, thenable); } - return transition; -} - -function handleAsyncAction(transition, thenable) { - { - // This is an async action. - entangleAsyncAction(transition, thenable); + if (prevOnStartTransitionFinish !== null) { + prevOnStartTransitionFinish(transition, returnValue); } -} +}; -function notifyTransitionCallbacks(transition, returnValue) { - var callbacks = transition._callbacks; - callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); +function requestCurrentTransition() { + return ReactSharedInternals.T; } // When retrying a Suspense/Offscreen boundary, we restore the cache that was // used during the previous render by placing it here, on the stack. @@ -31565,7 +31582,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition return root; } -var ReactVersion = '19.0.0-www-classic-fa9aeb29'; +var ReactVersion = '19.0.0-www-classic-5fdc5359'; function createPortal$1(children, containerInfo, // TODO: figure out the API for cross-renderer implementation. implementation) { diff --git a/compiled/facebook-www/ReactDOMTesting-dev.modern.js b/compiled/facebook-www/ReactDOMTesting-dev.modern.js index ca1a3fc5be36b..04196a5f92395 100644 --- a/compiled/facebook-www/ReactDOMTesting-dev.modern.js +++ b/compiled/facebook-www/ReactDOMTesting-dev.modern.js @@ -12795,9 +12795,7 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var prevState = actionQueue.state; // This is a fork of startTransition var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; ReactSharedInternals.T = currentTransition; { @@ -12810,11 +12808,15 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { try { var returnValue = action(prevState, payload); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } if (returnValue !== null && typeof returnValue === 'object' && // $FlowFixMe[method-unbinding] typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Attach a listener to read the return state of the action. As soon as + var thenable = returnValue; // Attach a listener to read the return state of the action. As soon as // this resolves, we can run the next action in the sequence. thenable.then(function (nextState) { @@ -13399,9 +13401,7 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op var previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; { // We don't really need to use an optimistic update here, because we @@ -13427,7 +13427,12 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op try { if (enableAsyncActions) { - var returnValue = callback(); // Check if we're inside an async action scope. If so, we'll entangle + var returnValue = callback(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } // Check if we're inside an async action scope. If so, we'll entangle // this new action with the existing scope. // // If we're not already inside an async action scope, and this action is @@ -13436,9 +13441,9 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op // In the async case, the resulting render will suspend until the async // action scope has finished. + if (returnValue !== null && typeof returnValue === 'object' && typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Create a thenable that resolves to `finishedState` once the async + var thenable = returnValue; // Create a thenable that resolves to `finishedState` once the async // action has completed. var thenableForFinishedState = chainThenableValue(thenable, finishedState); @@ -20460,30 +20465,42 @@ function popCacheProvider(workInProgress, cache) { popProvider(CacheContext, workInProgress); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - - if (transition !== null) { - // Whenever a transition update is scheduled, register a callback on the - // transition object so we can get the return value of the scope function. - transition._callbacks.add(handleAsyncAction); +// the shared internals object. This is used by the isomorphic implementation of +// startTransition to compose all the startTransitions together. +// +// function startTransition(fn) { +// return startTransitionDOM(() => { +// return startTransitionART(() => { +// return startTransitionThreeFiber(() => { +// // and so on... +// return fn(); +// }); +// }); +// }); +// } +// +// Currently we only compose together the code that runs at the end of each +// startTransition, because for now that's sufficient — the part that sets +// isTransition=true on the stack uses a separate shared internal field. But +// really we should delete the shared field and track isTransition per +// reconciler. Leaving this for a future PR. + +var prevOnStartTransitionFinish = ReactSharedInternals.S; + +ReactSharedInternals.S = function onStartTransitionFinishForReconciler(transition, returnValue) { + if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { + // This is an async action + var thenable = returnValue; + entangleAsyncAction(transition, thenable); } - return transition; -} - -function handleAsyncAction(transition, thenable) { - { - // This is an async action. - entangleAsyncAction(transition, thenable); + if (prevOnStartTransitionFinish !== null) { + prevOnStartTransitionFinish(transition, returnValue); } -} +}; -function notifyTransitionCallbacks(transition, returnValue) { - var callbacks = transition._callbacks; - callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); +function requestCurrentTransition() { + return ReactSharedInternals.T; } // When retrying a Suspense/Offscreen boundary, we restore the cache that was // used during the previous render by placing it here, on the stack. @@ -30740,7 +30757,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition return root; } -var ReactVersion = '19.0.0-www-modern-87db12a9'; +var ReactVersion = '19.0.0-www-modern-12f89c28'; function createPortal$1(children, containerInfo, // TODO: figure out the API for cross-renderer implementation. implementation) { diff --git a/compiled/facebook-www/ReactDOMTesting-prod.classic.js b/compiled/facebook-www/ReactDOMTesting-prod.classic.js index 83118b02d852e..c3b7437fde745 100644 --- a/compiled/facebook-www/ReactDOMTesting-prod.classic.js +++ b/compiled/facebook-www/ReactDOMTesting-prod.classic.js @@ -3829,16 +3829,18 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var action = actionQueue.action, prevState = actionQueue.state, prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; setPendingState(!0); try { - var returnValue = action(prevState, payload); + var returnValue = action(prevState, payload), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then - ? (notifyTransitionCallbacks(currentTransition, returnValue), - returnValue.then( + ? (returnValue.then( function (nextState) { actionQueue.state = nextState; finishRunningActionStateAction( @@ -4182,7 +4184,7 @@ function startTransition( Internals.p = 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8; var prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; dispatchOptimisticSetState(fiber, !1, queue, pendingState); enableTransitionTracing && @@ -4191,13 +4193,15 @@ function startTransition( ((currentTransition.name = options.name), (currentTransition.startTime = now())); try { - var returnValue = callback(); + var returnValue = callback(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); if ( null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then ) { - notifyTransitionCallbacks(currentTransition, returnValue); var thenableForFinishedState = chainThenableValue( returnValue, finishedState @@ -4267,7 +4271,6 @@ function ensureFormComponentIsStateful(formFiber) { return existingStateHook; } function requestFormReset$1(formFiber) { - requestCurrentTransition(); var resetStateQueue = ensureFormComponentIsStateful(formFiber).next.queue; dispatchSetState(formFiber, resetStateQueue, {}); } @@ -4358,7 +4361,6 @@ function dispatchSetState(fiber, queue, action) { } } function dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) { - requestCurrentTransition(); action = { lane: 2, revertLane: requestTransitionLane(), @@ -7621,19 +7623,15 @@ function releaseCache(cache) { cache.controller.abort(); }); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - null !== transition && transition._callbacks.add(handleAsyncAction); - return transition; -} -function handleAsyncAction(transition, thenable) { - entangleAsyncAction(transition, thenable); -} -function notifyTransitionCallbacks(transition, returnValue) { - transition._callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); -} +var prevOnStartTransitionFinish = ReactSharedInternals.S; +ReactSharedInternals.S = function (transition, returnValue) { + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + entangleAsyncAction(transition, returnValue); + null !== prevOnStartTransitionFinish && + prevOnStartTransitionFinish(transition, returnValue); +}; var resumedCache = createCursor(null), transitionStack = createCursor(null); function peekCacheFromPool() { @@ -11116,7 +11114,7 @@ function requestUpdateLane(fiber) { ? 2 : 0 !== (executionContext & 2) && 0 !== workInProgressRootRenderLanes ? workInProgressRootRenderLanes & -workInProgressRootRenderLanes - : null !== requestCurrentTransition() + : null !== ReactSharedInternals.T ? ((fiber = currentEntangledLane), 0 !== fiber ? fiber : requestTransitionLane()) : resolveUpdatePriority(); @@ -17399,7 +17397,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1776 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "19.0.0-www-classic-9e4355d6", + version: "19.0.0-www-classic-6f2dd7b5", rendererPackageName: "react-dom" }; var internals$jscomp$inline_2225 = { @@ -17429,7 +17427,7 @@ var internals$jscomp$inline_2225 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "19.0.0-www-classic-9e4355d6" + reconcilerVersion: "19.0.0-www-classic-6f2dd7b5" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_2226 = __REACT_DEVTOOLS_GLOBAL_HOOK__; @@ -18084,4 +18082,4 @@ exports.useFormState = function (action, initialState, permalink) { exports.useFormStatus = function () { return ReactSharedInternals.H.useHostTransitionStatus(); }; -exports.version = "19.0.0-www-classic-9e4355d6"; +exports.version = "19.0.0-www-classic-6f2dd7b5"; diff --git a/compiled/facebook-www/ReactDOMTesting-prod.modern.js b/compiled/facebook-www/ReactDOMTesting-prod.modern.js index aaab1d1ddc2f8..0192bfbc3457c 100644 --- a/compiled/facebook-www/ReactDOMTesting-prod.modern.js +++ b/compiled/facebook-www/ReactDOMTesting-prod.modern.js @@ -3676,16 +3676,18 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var action = actionQueue.action, prevState = actionQueue.state, prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; setPendingState(!0); try { - var returnValue = action(prevState, payload); + var returnValue = action(prevState, payload), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then - ? (notifyTransitionCallbacks(currentTransition, returnValue), - returnValue.then( + ? (returnValue.then( function (nextState) { actionQueue.state = nextState; finishRunningActionStateAction( @@ -4029,7 +4031,7 @@ function startTransition( Internals.p = 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8; var prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; dispatchOptimisticSetState(fiber, !1, queue, pendingState); enableTransitionTracing && @@ -4038,13 +4040,15 @@ function startTransition( ((currentTransition.name = options.name), (currentTransition.startTime = now())); try { - var returnValue = callback(); + var returnValue = callback(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); if ( null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then ) { - notifyTransitionCallbacks(currentTransition, returnValue); var thenableForFinishedState = chainThenableValue( returnValue, finishedState @@ -4114,7 +4118,6 @@ function ensureFormComponentIsStateful(formFiber) { return existingStateHook; } function requestFormReset$1(formFiber) { - requestCurrentTransition(); var resetStateQueue = ensureFormComponentIsStateful(formFiber).next.queue; dispatchSetState(formFiber, resetStateQueue, {}); } @@ -4205,7 +4208,6 @@ function dispatchSetState(fiber, queue, action) { } } function dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) { - requestCurrentTransition(); action = { lane: 2, revertLane: requestTransitionLane(), @@ -7222,19 +7224,15 @@ function releaseCache(cache) { cache.controller.abort(); }); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - null !== transition && transition._callbacks.add(handleAsyncAction); - return transition; -} -function handleAsyncAction(transition, thenable) { - entangleAsyncAction(transition, thenable); -} -function notifyTransitionCallbacks(transition, returnValue) { - transition._callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); -} +var prevOnStartTransitionFinish = ReactSharedInternals.S; +ReactSharedInternals.S = function (transition, returnValue) { + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + entangleAsyncAction(transition, returnValue); + null !== prevOnStartTransitionFinish && + prevOnStartTransitionFinish(transition, returnValue); +}; var resumedCache = createCursor(null), transitionStack = createCursor(null); function peekCacheFromPool() { @@ -10675,7 +10673,7 @@ var legacyErrorBoundariesThatAlreadyFailed = null, function requestUpdateLane() { if (0 !== (executionContext & 2) && 0 !== workInProgressRootRenderLanes) return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; - if (null !== requestCurrentTransition()) { + if (null !== ReactSharedInternals.T) { var actionScopeLane = currentEntangledLane; return 0 !== actionScopeLane ? actionScopeLane : requestTransitionLane(); } @@ -16821,7 +16819,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1743 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "19.0.0-www-modern-a9ff0c5d", + version: "19.0.0-www-modern-bd8ef997", rendererPackageName: "react-dom" }; var internals$jscomp$inline_2217 = { @@ -16851,7 +16849,7 @@ var internals$jscomp$inline_2217 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "19.0.0-www-modern-a9ff0c5d" + reconcilerVersion: "19.0.0-www-modern-bd8ef997" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_2218 = __REACT_DEVTOOLS_GLOBAL_HOOK__; @@ -17376,4 +17374,4 @@ exports.useFormState = function (action, initialState, permalink) { exports.useFormStatus = function () { return ReactSharedInternals.H.useHostTransitionStatus(); }; -exports.version = "19.0.0-www-modern-a9ff0c5d"; +exports.version = "19.0.0-www-modern-bd8ef997"; diff --git a/compiled/facebook-www/ReactReconciler-dev.classic.js b/compiled/facebook-www/ReactReconciler-dev.classic.js index 21378c93620c2..fb4e3afbac998 100644 --- a/compiled/facebook-www/ReactReconciler-dev.classic.js +++ b/compiled/facebook-www/ReactReconciler-dev.classic.js @@ -9844,9 +9844,7 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var prevState = actionQueue.state; // This is a fork of startTransition var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; ReactSharedInternals.T = currentTransition; { @@ -9859,11 +9857,15 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { try { var returnValue = action(prevState, payload); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } if (returnValue !== null && typeof returnValue === 'object' && // $FlowFixMe[method-unbinding] typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Attach a listener to read the return state of the action. As soon as + var thenable = returnValue; // Attach a listener to read the return state of the action. As soon as // this resolves, we can run the next action in the sequence. thenable.then(function (nextState) { @@ -10448,9 +10450,7 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op var previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; { // We don't really need to use an optimistic update here, because we @@ -10476,7 +10476,12 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op try { if (enableAsyncActions) { - var returnValue = callback(); // Check if we're inside an async action scope. If so, we'll entangle + var returnValue = callback(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } // Check if we're inside an async action scope. If so, we'll entangle // this new action with the existing scope. // // If we're not already inside an async action scope, and this action is @@ -10485,9 +10490,9 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op // In the async case, the resulting render will suspend until the async // action scope has finished. + if (returnValue !== null && typeof returnValue === 'object' && typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Create a thenable that resolves to `finishedState` once the async + var thenable = returnValue; // Create a thenable that resolves to `finishedState` once the async // action has completed. var thenableForFinishedState = chainThenableValue(thenable, finishedState); @@ -17802,30 +17807,42 @@ function popCacheProvider(workInProgress, cache) { popProvider(CacheContext, workInProgress); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - - if (transition !== null) { - // Whenever a transition update is scheduled, register a callback on the - // transition object so we can get the return value of the scope function. - transition._callbacks.add(handleAsyncAction); +// the shared internals object. This is used by the isomorphic implementation of +// startTransition to compose all the startTransitions together. +// +// function startTransition(fn) { +// return startTransitionDOM(() => { +// return startTransitionART(() => { +// return startTransitionThreeFiber(() => { +// // and so on... +// return fn(); +// }); +// }); +// }); +// } +// +// Currently we only compose together the code that runs at the end of each +// startTransition, because for now that's sufficient — the part that sets +// isTransition=true on the stack uses a separate shared internal field. But +// really we should delete the shared field and track isTransition per +// reconciler. Leaving this for a future PR. + +var prevOnStartTransitionFinish = ReactSharedInternals.S; + +ReactSharedInternals.S = function onStartTransitionFinishForReconciler(transition, returnValue) { + if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { + // This is an async action + var thenable = returnValue; + entangleAsyncAction(transition, thenable); } - return transition; -} - -function handleAsyncAction(transition, thenable) { - { - // This is an async action. - entangleAsyncAction(transition, thenable); + if (prevOnStartTransitionFinish !== null) { + prevOnStartTransitionFinish(transition, returnValue); } -} +}; -function notifyTransitionCallbacks(transition, returnValue) { - var callbacks = transition._callbacks; - callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); +function requestCurrentTransition() { + return ReactSharedInternals.T; } // When retrying a Suspense/Offscreen boundary, we restore the cache that was // used during the previous render by placing it here, on the stack. @@ -28797,7 +28814,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition return root; } -var ReactVersion = '19.0.0-www-classic-2e0a93db'; +var ReactVersion = '19.0.0-www-classic-8cca14b7'; /* * The `'' + value` pattern (used in perf-sensitive code) throws for Symbol diff --git a/compiled/facebook-www/ReactReconciler-dev.modern.js b/compiled/facebook-www/ReactReconciler-dev.modern.js index cae87c6aef0fc..daacba11953fb 100644 --- a/compiled/facebook-www/ReactReconciler-dev.modern.js +++ b/compiled/facebook-www/ReactReconciler-dev.modern.js @@ -9635,9 +9635,7 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var prevState = actionQueue.state; // This is a fork of startTransition var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; ReactSharedInternals.T = currentTransition; { @@ -9650,11 +9648,15 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { try { var returnValue = action(prevState, payload); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } if (returnValue !== null && typeof returnValue === 'object' && // $FlowFixMe[method-unbinding] typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Attach a listener to read the return state of the action. As soon as + var thenable = returnValue; // Attach a listener to read the return state of the action. As soon as // this resolves, we can run the next action in the sequence. thenable.then(function (nextState) { @@ -10239,9 +10241,7 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op var previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; { // We don't really need to use an optimistic update here, because we @@ -10267,7 +10267,12 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op try { if (enableAsyncActions) { - var returnValue = callback(); // Check if we're inside an async action scope. If so, we'll entangle + var returnValue = callback(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } // Check if we're inside an async action scope. If so, we'll entangle // this new action with the existing scope. // // If we're not already inside an async action scope, and this action is @@ -10276,9 +10281,9 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op // In the async case, the resulting render will suspend until the async // action scope has finished. + if (returnValue !== null && typeof returnValue === 'object' && typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Create a thenable that resolves to `finishedState` once the async + var thenable = returnValue; // Create a thenable that resolves to `finishedState` once the async // action has completed. var thenableForFinishedState = chainThenableValue(thenable, finishedState); @@ -17329,30 +17334,42 @@ function popCacheProvider(workInProgress, cache) { popProvider(CacheContext, workInProgress); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - - if (transition !== null) { - // Whenever a transition update is scheduled, register a callback on the - // transition object so we can get the return value of the scope function. - transition._callbacks.add(handleAsyncAction); +// the shared internals object. This is used by the isomorphic implementation of +// startTransition to compose all the startTransitions together. +// +// function startTransition(fn) { +// return startTransitionDOM(() => { +// return startTransitionART(() => { +// return startTransitionThreeFiber(() => { +// // and so on... +// return fn(); +// }); +// }); +// }); +// } +// +// Currently we only compose together the code that runs at the end of each +// startTransition, because for now that's sufficient — the part that sets +// isTransition=true on the stack uses a separate shared internal field. But +// really we should delete the shared field and track isTransition per +// reconciler. Leaving this for a future PR. + +var prevOnStartTransitionFinish = ReactSharedInternals.S; + +ReactSharedInternals.S = function onStartTransitionFinishForReconciler(transition, returnValue) { + if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { + // This is an async action + var thenable = returnValue; + entangleAsyncAction(transition, thenable); } - return transition; -} - -function handleAsyncAction(transition, thenable) { - { - // This is an async action. - entangleAsyncAction(transition, thenable); + if (prevOnStartTransitionFinish !== null) { + prevOnStartTransitionFinish(transition, returnValue); } -} +}; -function notifyTransitionCallbacks(transition, returnValue) { - var callbacks = transition._callbacks; - callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); +function requestCurrentTransition() { + return ReactSharedInternals.T; } // When retrying a Suspense/Offscreen boundary, we restore the cache that was // used during the previous render by placing it here, on the stack. @@ -28063,7 +28080,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition return root; } -var ReactVersion = '19.0.0-www-modern-8a76092c'; +var ReactVersion = '19.0.0-www-modern-62a5edf0'; /* * The `'' + value` pattern (used in perf-sensitive code) throws for Symbol diff --git a/compiled/facebook-www/ReactReconciler-prod.classic.js b/compiled/facebook-www/ReactReconciler-prod.classic.js index 1724665e5b6b5..e236e358d0481 100644 --- a/compiled/facebook-www/ReactReconciler-prod.classic.js +++ b/compiled/facebook-www/ReactReconciler-prod.classic.js @@ -2890,16 +2890,18 @@ module.exports = function ($$$config) { var action = actionQueue.action, prevState = actionQueue.state, prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; setPendingState(!0); try { - var returnValue = action(prevState, payload); + var returnValue = action(prevState, payload), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then - ? (notifyTransitionCallbacks(currentTransition, returnValue), - returnValue.then( + ? (returnValue.then( function (nextState) { actionQueue.state = nextState; finishRunningActionStateAction( @@ -3232,7 +3234,7 @@ module.exports = function ($$$config) { 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8 ); var prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; dispatchOptimisticSetState(fiber, !1, queue, pendingState); enableTransitionTracing && @@ -3241,13 +3243,15 @@ module.exports = function ($$$config) { ((currentTransition.name = options.name), (currentTransition.startTime = now())); try { - var returnValue = callback(); + var returnValue = callback(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); if ( null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then ) { - notifyTransitionCallbacks(currentTransition, returnValue); var thenableForFinishedState = chainThenableValue( returnValue, finishedState @@ -3392,7 +3396,6 @@ module.exports = function ($$$config) { queue, action ) { - requestCurrentTransition(); action = { lane: 2, revertLane: requestTransitionLane(), @@ -6298,19 +6301,6 @@ module.exports = function ($$$config) { cache.controller.abort(); }); } - function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - null !== transition && transition._callbacks.add(handleAsyncAction); - return transition; - } - function handleAsyncAction(transition, thenable) { - entangleAsyncAction(transition, thenable); - } - function notifyTransitionCallbacks(transition, returnValue) { - transition._callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); - } function peekCacheFromPool() { var cacheResumedFromPreviousRender = resumedCache.current; return null !== cacheResumedFromPreviousRender @@ -9821,7 +9811,7 @@ module.exports = function ($$$config) { ? 2 : 0 !== (executionContext & 2) && 0 !== workInProgressRootRenderLanes ? workInProgressRootRenderLanes & -workInProgressRootRenderLanes - : null !== requestCurrentTransition() + : null !== ReactSharedInternals.T ? ((fiber = currentEntangledLane), 0 !== fiber ? fiber : requestTransitionLane()) : resolveUpdatePriority(); @@ -12198,7 +12188,16 @@ module.exports = function ($$$config) { _currentValue2: null, _threadCount: 0 }, - resumedCache = createCursor(null), + prevOnStartTransitionFinish = ReactSharedInternals.S; + ReactSharedInternals.S = function (transition, returnValue) { + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + entangleAsyncAction(transition, returnValue); + null !== prevOnStartTransitionFinish && + prevOnStartTransitionFinish(transition, returnValue); + }; + var resumedCache = createCursor(null), transitionStack = createCursor(null), emptyObject = {}, offscreenSubtreeIsHidden = !1, @@ -12646,7 +12645,7 @@ module.exports = function ($$$config) { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "19.0.0-www-classic-1be1baa9" + reconcilerVersion: "19.0.0-www-classic-dfc73dd2" }; if ("undefined" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) devToolsConfig = !1; @@ -12706,7 +12705,6 @@ module.exports = function ($$$config) { null === action ? noop : function () { - requestCurrentTransition(); var resetStateQueue = ensureFormComponentIsStateful(formFiber).next.queue; dispatchSetState(formFiber, resetStateQueue, {}); diff --git a/compiled/facebook-www/ReactReconciler-prod.modern.js b/compiled/facebook-www/ReactReconciler-prod.modern.js index 8a3327a033257..25b3498dfb762 100644 --- a/compiled/facebook-www/ReactReconciler-prod.modern.js +++ b/compiled/facebook-www/ReactReconciler-prod.modern.js @@ -2750,16 +2750,18 @@ module.exports = function ($$$config) { var action = actionQueue.action, prevState = actionQueue.state, prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; setPendingState(!0); try { - var returnValue = action(prevState, payload); + var returnValue = action(prevState, payload), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then - ? (notifyTransitionCallbacks(currentTransition, returnValue), - returnValue.then( + ? (returnValue.then( function (nextState) { actionQueue.state = nextState; finishRunningActionStateAction( @@ -3092,7 +3094,7 @@ module.exports = function ($$$config) { 0 !== previousPriority && 8 > previousPriority ? previousPriority : 8 ); var prevTransition = ReactSharedInternals.T, - currentTransition = { _callbacks: new Set() }; + currentTransition = {}; ReactSharedInternals.T = currentTransition; dispatchOptimisticSetState(fiber, !1, queue, pendingState); enableTransitionTracing && @@ -3101,13 +3103,15 @@ module.exports = function ($$$config) { ((currentTransition.name = options.name), (currentTransition.startTime = now())); try { - var returnValue = callback(); + var returnValue = callback(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); if ( null !== returnValue && "object" === typeof returnValue && "function" === typeof returnValue.then ) { - notifyTransitionCallbacks(currentTransition, returnValue); var thenableForFinishedState = chainThenableValue( returnValue, finishedState @@ -3252,7 +3256,6 @@ module.exports = function ($$$config) { queue, action ) { - requestCurrentTransition(); action = { lane: 2, revertLane: requestTransitionLane(), @@ -5932,19 +5935,6 @@ module.exports = function ($$$config) { cache.controller.abort(); }); } - function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - null !== transition && transition._callbacks.add(handleAsyncAction); - return transition; - } - function handleAsyncAction(transition, thenable) { - entangleAsyncAction(transition, thenable); - } - function notifyTransitionCallbacks(transition, returnValue) { - transition._callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); - } function peekCacheFromPool() { var cacheResumedFromPreviousRender = resumedCache.current; return null !== cacheResumedFromPreviousRender @@ -9396,7 +9386,7 @@ module.exports = function ($$$config) { function requestUpdateLane() { if (0 !== (executionContext & 2) && 0 !== workInProgressRootRenderLanes) return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; - if (null !== requestCurrentTransition()) { + if (null !== ReactSharedInternals.T) { var actionScopeLane = currentEntangledLane; return 0 !== actionScopeLane ? actionScopeLane : requestTransitionLane(); } @@ -11721,7 +11711,16 @@ module.exports = function ($$$config) { _currentValue2: null, _threadCount: 0 }, - resumedCache = createCursor(null), + prevOnStartTransitionFinish = ReactSharedInternals.S; + ReactSharedInternals.S = function (transition, returnValue) { + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + entangleAsyncAction(transition, returnValue); + null !== prevOnStartTransitionFinish && + prevOnStartTransitionFinish(transition, returnValue); + }; + var resumedCache = createCursor(null), transitionStack = createCursor(null), emptyObject = {}, offscreenSubtreeIsHidden = !1, @@ -12159,7 +12158,7 @@ module.exports = function ($$$config) { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "19.0.0-www-modern-b311bf05" + reconcilerVersion: "19.0.0-www-modern-5c642108" }; if ("undefined" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) devToolsConfig = !1; @@ -12219,7 +12218,6 @@ module.exports = function ($$$config) { null === action ? noop : function () { - requestCurrentTransition(); var resetStateQueue = ensureFormComponentIsStateful(formFiber).next.queue; dispatchSetState(formFiber, resetStateQueue, {}); diff --git a/compiled/facebook-www/ReactTestRenderer-dev.classic.js b/compiled/facebook-www/ReactTestRenderer-dev.classic.js index 289f76db7aee1..259d303319e0a 100644 --- a/compiled/facebook-www/ReactTestRenderer-dev.classic.js +++ b/compiled/facebook-www/ReactTestRenderer-dev.classic.js @@ -8374,9 +8374,7 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var prevState = actionQueue.state; // This is a fork of startTransition var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; ReactSharedInternals.T = currentTransition; { @@ -8389,11 +8387,15 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { try { var returnValue = action(prevState, payload); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } if (returnValue !== null && typeof returnValue === 'object' && // $FlowFixMe[method-unbinding] typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Attach a listener to read the return state of the action. As soon as + var thenable = returnValue; // Attach a listener to read the return state of the action. As soon as // this resolves, we can run the next action in the sequence. thenable.then(function (nextState) { @@ -8911,9 +8913,7 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op var previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; { // We don't really need to use an optimistic update here, because we @@ -8932,7 +8932,12 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op try { if (enableAsyncActions) { - var returnValue = callback(); // Check if we're inside an async action scope. If so, we'll entangle + var returnValue = callback(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } // Check if we're inside an async action scope. If so, we'll entangle // this new action with the existing scope. // // If we're not already inside an async action scope, and this action is @@ -8941,9 +8946,9 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op // In the async case, the resulting render will suspend until the async // action scope has finished. + if (returnValue !== null && typeof returnValue === 'object' && typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Create a thenable that resolves to `finishedState` once the async + var thenable = returnValue; // Create a thenable that resolves to `finishedState` once the async // action has completed. var thenableForFinishedState = chainThenableValue(thenable, finishedState); @@ -14885,30 +14890,42 @@ function popCacheProvider(workInProgress, cache) { popProvider(CacheContext, workInProgress); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - - if (transition !== null) { - // Whenever a transition update is scheduled, register a callback on the - // transition object so we can get the return value of the scope function. - transition._callbacks.add(handleAsyncAction); +// the shared internals object. This is used by the isomorphic implementation of +// startTransition to compose all the startTransitions together. +// +// function startTransition(fn) { +// return startTransitionDOM(() => { +// return startTransitionART(() => { +// return startTransitionThreeFiber(() => { +// // and so on... +// return fn(); +// }); +// }); +// }); +// } +// +// Currently we only compose together the code that runs at the end of each +// startTransition, because for now that's sufficient — the part that sets +// isTransition=true on the stack uses a separate shared internal field. But +// really we should delete the shared field and track isTransition per +// reconciler. Leaving this for a future PR. + +var prevOnStartTransitionFinish = ReactSharedInternals.S; + +ReactSharedInternals.S = function onStartTransitionFinishForReconciler(transition, returnValue) { + if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { + // This is an async action + var thenable = returnValue; + entangleAsyncAction(transition, thenable); } - return transition; -} - -function handleAsyncAction(transition, thenable) { - { - // This is an async action. - entangleAsyncAction(transition, thenable); + if (prevOnStartTransitionFinish !== null) { + prevOnStartTransitionFinish(transition, returnValue); } -} +}; -function notifyTransitionCallbacks(transition, returnValue) { - var callbacks = transition._callbacks; - callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); +function requestCurrentTransition() { + return ReactSharedInternals.T; } // When retrying a Suspense/Offscreen boundary, we restore the cache that was // used during the previous render by placing it here, on the stack. @@ -23277,7 +23294,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition return root; } -var ReactVersion = '19.0.0-www-classic-20bcb906'; +var ReactVersion = '19.0.0-www-classic-71c16c3b'; /* * The `'' + value` pattern (used in perf-sensitive code) throws for Symbol diff --git a/compiled/facebook-www/ReactTestRenderer-dev.modern.js b/compiled/facebook-www/ReactTestRenderer-dev.modern.js index 0469514dc2a60..49f429da4a410 100644 --- a/compiled/facebook-www/ReactTestRenderer-dev.modern.js +++ b/compiled/facebook-www/ReactTestRenderer-dev.modern.js @@ -8374,9 +8374,7 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { var prevState = actionQueue.state; // This is a fork of startTransition var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; ReactSharedInternals.T = currentTransition; { @@ -8389,11 +8387,15 @@ function runActionStateAction(actionQueue, setPendingState, setState, payload) { try { var returnValue = action(prevState, payload); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } if (returnValue !== null && typeof returnValue === 'object' && // $FlowFixMe[method-unbinding] typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Attach a listener to read the return state of the action. As soon as + var thenable = returnValue; // Attach a listener to read the return state of the action. As soon as // this resolves, we can run the next action in the sequence. thenable.then(function (nextState) { @@ -8911,9 +8913,7 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op var previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); var prevTransition = ReactSharedInternals.T; - var currentTransition = { - _callbacks: new Set() - }; + var currentTransition = {}; { // We don't really need to use an optimistic update here, because we @@ -8932,7 +8932,12 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op try { if (enableAsyncActions) { - var returnValue = callback(); // Check if we're inside an async action scope. If so, we'll entangle + var returnValue = callback(); + var onStartTransitionFinish = ReactSharedInternals.S; + + if (onStartTransitionFinish !== null) { + onStartTransitionFinish(currentTransition, returnValue); + } // Check if we're inside an async action scope. If so, we'll entangle // this new action with the existing scope. // // If we're not already inside an async action scope, and this action is @@ -8941,9 +8946,9 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op // In the async case, the resulting render will suspend until the async // action scope has finished. + if (returnValue !== null && typeof returnValue === 'object' && typeof returnValue.then === 'function') { - var thenable = returnValue; - notifyTransitionCallbacks(currentTransition, thenable); // Create a thenable that resolves to `finishedState` once the async + var thenable = returnValue; // Create a thenable that resolves to `finishedState` once the async // action has completed. var thenableForFinishedState = chainThenableValue(thenable, finishedState); @@ -14885,30 +14890,42 @@ function popCacheProvider(workInProgress, cache) { popProvider(CacheContext, workInProgress); } -function requestCurrentTransition() { - var transition = ReactSharedInternals.T; - - if (transition !== null) { - // Whenever a transition update is scheduled, register a callback on the - // transition object so we can get the return value of the scope function. - transition._callbacks.add(handleAsyncAction); +// the shared internals object. This is used by the isomorphic implementation of +// startTransition to compose all the startTransitions together. +// +// function startTransition(fn) { +// return startTransitionDOM(() => { +// return startTransitionART(() => { +// return startTransitionThreeFiber(() => { +// // and so on... +// return fn(); +// }); +// }); +// }); +// } +// +// Currently we only compose together the code that runs at the end of each +// startTransition, because for now that's sufficient — the part that sets +// isTransition=true on the stack uses a separate shared internal field. But +// really we should delete the shared field and track isTransition per +// reconciler. Leaving this for a future PR. + +var prevOnStartTransitionFinish = ReactSharedInternals.S; + +ReactSharedInternals.S = function onStartTransitionFinishForReconciler(transition, returnValue) { + if (typeof returnValue === 'object' && returnValue !== null && typeof returnValue.then === 'function') { + // This is an async action + var thenable = returnValue; + entangleAsyncAction(transition, thenable); } - return transition; -} - -function handleAsyncAction(transition, thenable) { - { - // This is an async action. - entangleAsyncAction(transition, thenable); + if (prevOnStartTransitionFinish !== null) { + prevOnStartTransitionFinish(transition, returnValue); } -} +}; -function notifyTransitionCallbacks(transition, returnValue) { - var callbacks = transition._callbacks; - callbacks.forEach(function (callback) { - return callback(transition, returnValue); - }); +function requestCurrentTransition() { + return ReactSharedInternals.T; } // When retrying a Suspense/Offscreen boundary, we restore the cache that was // used during the previous render by placing it here, on the stack. @@ -23277,7 +23294,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition return root; } -var ReactVersion = '19.0.0-www-modern-20bcb906'; +var ReactVersion = '19.0.0-www-modern-71c16c3b'; /* * The `'' + value` pattern (used in perf-sensitive code) throws for Symbol