Fix: useTransition after use gets stuck in pending state (#29670)

When a component suspends with `use`, we switch to the "re-render"
dispatcher during the subsequent render attempt, so that we can reuse
the work from the initial attempt. However, once we run out of hooks
from the previous attempt, we should switch back to the regular "update"
dispatcher.

This is conceptually the same fix as the one introduced in
https://github.com/facebook/react/pull/26232. That fix only accounted
for initial mount, but the useTransition regression test added in
f82973302b3f490ec120c3b102e8c3792452dfc9 illustrates that we need to
handle updates, too.

The issue affects more than just useTransition but because most of the
behavior between the "re-render" and "update" dispatchers is the same
it's hard to contrive other scenarios in a test, which is probably why
it took so long for someone to notice.

Closes #28923 and #29209

---------

Co-authored-by: eps1lon <sebastian.silbermann@vercel.com>

DiffTrain build for commit https://github.com/facebook/react/commit/adbec0c25aff07f04b0678679554505ba2813168.
This commit is contained in:
acdlite
2024-05-31 21:57:58 +00:00
co-authored by eps1lon
parent 6ce16b6bb0
commit 9f66192fd2
13 changed files with 182 additions and 80 deletions
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<75b6042ec2df1ed24f9d3d5c89c9b994>>
* @generated SignedSource<<843340fb623449714d5807ec34c388a7>>
*/
'use strict';
@@ -7843,15 +7843,39 @@ function useThenable(thenable) {
thenableState = createThenableState();
}
var result = trackUsedThenable(thenableState, thenable, index);
var result = trackUsedThenable(thenableState, thenable, index); // When something suspends with `use`, we replay the component with the
// "re-render" dispatcher instead of the "mount" or "update" dispatcher.
//
// But if there are additional hooks that occur after the `use` invocation
// that suspended, they wouldn't have been processed during the previous
// attempt. So after we invoke `use` again, we may need to switch from the
// "re-render" dispatcher back to the "mount" or "update" dispatcher. That's
// what the following logic accounts for.
//
// TODO: Theoretically this logic only needs to go into the rerender
// dispatcher. Could optimize, but probably not be worth it.
// This is the same logic as in updateWorkInProgressHook.
var workInProgressFiber = currentlyRenderingFiber$1;
var nextWorkInProgressHook = workInProgressHook === null ? // We're at the beginning of the list, so read from the first hook from
// the fiber.
workInProgressFiber.memoizedState : workInProgressHook.next;
if (nextWorkInProgressHook !== null) ; else {
// There are no remaining hooks from the previous attempt. We're no longer
// in "re-render" mode. Switch to the normal mount or update dispatcher.
//
// This is the same as the logic in renderWithHooks, except we don't bother
// to track the hook types debug information in this case (sufficient to
// only do that when nothing suspends).
var currentFiber = workInProgressFiber.alternate;
if (currentlyRenderingFiber$1.alternate === null && (workInProgressHook === null ? currentlyRenderingFiber$1.memoizedState === null : workInProgressHook.next === null)) {
// Initial render, and either this is the first time the component is
// called, or there were no Hooks called after this use() the previous
// time (perhaps because it threw). Subsequent Hook calls should use the
// mount dispatcher.
{
ReactSharedInternals.H = HooksDispatcherOnMountInDEV;
if (currentFiber !== null && currentFiber.memoizedState !== null) {
ReactSharedInternals.H = HooksDispatcherOnUpdateInDEV;
} else {
ReactSharedInternals.H = HooksDispatcherOnMountInDEV;
}
}
}
@@ -23471,7 +23495,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition
return root;
}
var ReactVersion = '19.0.0-rc-5c420e3824-20240531';
var ReactVersion = '19.0.0-rc-adbec0c25a-20240531';
/*
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<0c59fb69281d1d909e6de56cf4430600>>
* @generated SignedSource<<905cc175856264e313ce8da1a2a4238b>>
*/
"use strict";
@@ -2478,11 +2478,16 @@ function useThenable(thenable) {
thenableIndexCounter += 1;
null === thenableState && (thenableState = []);
thenable = trackUsedThenable(thenableState, thenable, index);
null === currentlyRenderingFiber$1.alternate &&
index = currentlyRenderingFiber$1;
null ===
(null === workInProgressHook
? null === currentlyRenderingFiber$1.memoizedState
: null === workInProgressHook.next) &&
(ReactSharedInternals.H = HooksDispatcherOnMount);
? index.memoizedState
: workInProgressHook.next) &&
((index = index.alternate),
(ReactSharedInternals.H =
null === index || null === index.memoizedState
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate));
return thenable;
}
function use(usable) {
@@ -9298,7 +9303,7 @@ var devToolsConfig$jscomp$inline_1047 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "19.0.0-rc-5c420e3824-20240531",
version: "19.0.0-rc-adbec0c25a-20240531",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1234 = {
@@ -9329,7 +9334,7 @@ var internals$jscomp$inline_1234 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-rc-5c420e3824-20240531"
reconcilerVersion: "19.0.0-rc-adbec0c25a-20240531"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1235 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<0a4e2053c331cd297df75b0c0a284142>>
* @generated SignedSource<<2ec503c3ec9cc5ba080608bcfdeb069e>>
*/
"use strict";
@@ -2566,11 +2566,16 @@ function useThenable(thenable) {
thenableIndexCounter += 1;
null === thenableState && (thenableState = []);
thenable = trackUsedThenable(thenableState, thenable, index);
null === currentlyRenderingFiber$1.alternate &&
index = currentlyRenderingFiber$1;
null ===
(null === workInProgressHook
? null === currentlyRenderingFiber$1.memoizedState
: null === workInProgressHook.next) &&
(ReactSharedInternals.H = HooksDispatcherOnMount);
? index.memoizedState
: workInProgressHook.next) &&
((index = index.alternate),
(ReactSharedInternals.H =
null === index || null === index.memoizedState
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate));
return thenable;
}
function use(usable) {
@@ -9920,7 +9925,7 @@ var devToolsConfig$jscomp$inline_1130 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "19.0.0-rc-5c420e3824-20240531",
version: "19.0.0-rc-adbec0c25a-20240531",
rendererPackageName: "react-test-renderer"
};
(function (internals) {
@@ -9964,7 +9969,7 @@ var devToolsConfig$jscomp$inline_1130 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-rc-5c420e3824-20240531"
reconcilerVersion: "19.0.0-rc-adbec0c25a-20240531"
});
exports._Scheduler = Scheduler;
exports.act = act;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<7e91f8a3315accc44974ba6d3012b60f>>
* @generated SignedSource<<4f7e50456e26afbb6944fc28181daa80>>
*/
'use strict';
@@ -24,7 +24,7 @@ if (
}
var dynamicFlagsUntyped = require('ReactNativeInternalFeatureFlags');
var ReactVersion = '19.0.0-rc-5c420e3824-20240531';
var ReactVersion = '19.0.0-rc-adbec0c25a-20240531';
// Re-export dynamic flags from the internal module.
var dynamicFlags = dynamicFlagsUntyped; // We destructure each value before re-exporting to avoid a dynamic look-up on
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<2a9cffe28b099cbbf68478af1f393ff1>>
* @generated SignedSource<<ab9c75532395cf1647c807f37789a8e4>>
*/
"use strict";
@@ -604,4 +604,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.0.0-rc-5c420e3824-20240531";
exports.version = "19.0.0-rc-adbec0c25a-20240531";
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<8ec2a6bbcfd86d30090ddd12c98add78>>
* @generated SignedSource<<9f5c8cb9b979f6b6f6c25611a0886ae3>>
*/
"use strict";
@@ -608,7 +608,7 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.0.0-rc-5c420e3824-20240531";
exports.version = "19.0.0-rc-adbec0c25a-20240531";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
@@ -1 +1 @@
5c420e3824859b33321b4bc9ce3119806fac56c2
adbec0c25aff07f04b0678679554505ba2813168
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<9b439e19fdffa2fa6e386a55c7f2dc88>>
* @generated SignedSource<<959db6b7a430088709ef79d2031321cd>>
*/
'use strict';
@@ -10583,15 +10583,39 @@ function useThenable(thenable) {
thenableState = createThenableState();
}
var result = trackUsedThenable(thenableState, thenable, index);
var result = trackUsedThenable(thenableState, thenable, index); // When something suspends with `use`, we replay the component with the
// "re-render" dispatcher instead of the "mount" or "update" dispatcher.
//
// But if there are additional hooks that occur after the `use` invocation
// that suspended, they wouldn't have been processed during the previous
// attempt. So after we invoke `use` again, we may need to switch from the
// "re-render" dispatcher back to the "mount" or "update" dispatcher. That's
// what the following logic accounts for.
//
// TODO: Theoretically this logic only needs to go into the rerender
// dispatcher. Could optimize, but probably not be worth it.
// This is the same logic as in updateWorkInProgressHook.
var workInProgressFiber = currentlyRenderingFiber$1;
var nextWorkInProgressHook = workInProgressHook === null ? // We're at the beginning of the list, so read from the first hook from
// the fiber.
workInProgressFiber.memoizedState : workInProgressHook.next;
if (nextWorkInProgressHook !== null) ; else {
// There are no remaining hooks from the previous attempt. We're no longer
// in "re-render" mode. Switch to the normal mount or update dispatcher.
//
// This is the same as the logic in renderWithHooks, except we don't bother
// to track the hook types debug information in this case (sufficient to
// only do that when nothing suspends).
var currentFiber = workInProgressFiber.alternate;
if (currentlyRenderingFiber$1.alternate === null && (workInProgressHook === null ? currentlyRenderingFiber$1.memoizedState === null : workInProgressHook.next === null)) {
// Initial render, and either this is the first time the component is
// called, or there were no Hooks called after this use() the previous
// time (perhaps because it threw). Subsequent Hook calls should use the
// mount dispatcher.
{
ReactSharedInternals.H = HooksDispatcherOnMountInDEV;
if (currentFiber !== null && currentFiber.memoizedState !== null) {
ReactSharedInternals.H = HooksDispatcherOnUpdateInDEV;
} else {
ReactSharedInternals.H = HooksDispatcherOnMountInDEV;
}
}
}
@@ -26206,7 +26230,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition
return root;
}
var ReactVersion = '19.0.0-rc-5c420e3824-20240531';
var ReactVersion = '19.0.0-rc-adbec0c25a-20240531';
/*
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<183c26ead26dbc699a54a97554f63097>>
* @generated SignedSource<<d4d79b9c46da5483403dab31cf882201>>
*/
"use strict";
@@ -3857,11 +3857,16 @@ function useThenable(thenable) {
thenableIndexCounter += 1;
null === thenableState && (thenableState = []);
thenable = trackUsedThenable(thenableState, thenable, index);
null === currentlyRenderingFiber$1.alternate &&
index = currentlyRenderingFiber$1;
null ===
(null === workInProgressHook
? null === currentlyRenderingFiber$1.memoizedState
: null === workInProgressHook.next) &&
(ReactSharedInternals.H = HooksDispatcherOnMount);
? index.memoizedState
: workInProgressHook.next) &&
((index = index.alternate),
(ReactSharedInternals.H =
null === index || null === index.memoizedState
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate));
return thenable;
}
function use(usable) {
@@ -10551,7 +10556,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1124 = {
findFiberByHostInstance: getInstanceFromNode,
bundleType: 0,
version: "19.0.0-rc-5c420e3824-20240531",
version: "19.0.0-rc-adbec0c25a-20240531",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -10594,7 +10599,7 @@ var internals$jscomp$inline_1350 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-rc-5c420e3824-20240531"
reconcilerVersion: "19.0.0-rc-adbec0c25a-20240531"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1351 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<ddb3340d112903f5a5cd915605b6d398>>
* @generated SignedSource<<8ccb12c6c371309a0a428e1772ab27d6>>
*/
"use strict";
@@ -3979,11 +3979,16 @@ function useThenable(thenable) {
thenableIndexCounter += 1;
null === thenableState && (thenableState = []);
thenable = trackUsedThenable(thenableState, thenable, index);
null === currentlyRenderingFiber$1.alternate &&
index = currentlyRenderingFiber$1;
null ===
(null === workInProgressHook
? null === currentlyRenderingFiber$1.memoizedState
: null === workInProgressHook.next) &&
(ReactSharedInternals.H = HooksDispatcherOnMount);
? index.memoizedState
: workInProgressHook.next) &&
((index = index.alternate),
(ReactSharedInternals.H =
null === index || null === index.memoizedState
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate));
return thenable;
}
function use(usable) {
@@ -11257,7 +11262,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1205 = {
findFiberByHostInstance: getInstanceFromNode,
bundleType: 0,
version: "19.0.0-rc-5c420e3824-20240531",
version: "19.0.0-rc-adbec0c25a-20240531",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -11313,7 +11318,7 @@ var roots = new Map(),
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-rc-5c420e3824-20240531"
reconcilerVersion: "19.0.0-rc-adbec0c25a-20240531"
});
exports.createPortal = function (children, containerTag) {
return createPortal$1(
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<a1f9c5101bc4dde19716134881514df8>>
* @generated SignedSource<<2f599e53bfb1d0ee6eaf5cd63bae3a13>>
*/
'use strict';
@@ -10766,15 +10766,39 @@ function useThenable(thenable) {
thenableState = createThenableState();
}
var result = trackUsedThenable(thenableState, thenable, index);
var result = trackUsedThenable(thenableState, thenable, index); // When something suspends with `use`, we replay the component with the
// "re-render" dispatcher instead of the "mount" or "update" dispatcher.
//
// But if there are additional hooks that occur after the `use` invocation
// that suspended, they wouldn't have been processed during the previous
// attempt. So after we invoke `use` again, we may need to switch from the
// "re-render" dispatcher back to the "mount" or "update" dispatcher. That's
// what the following logic accounts for.
//
// TODO: Theoretically this logic only needs to go into the rerender
// dispatcher. Could optimize, but probably not be worth it.
// This is the same logic as in updateWorkInProgressHook.
var workInProgressFiber = currentlyRenderingFiber$1;
var nextWorkInProgressHook = workInProgressHook === null ? // We're at the beginning of the list, so read from the first hook from
// the fiber.
workInProgressFiber.memoizedState : workInProgressHook.next;
if (nextWorkInProgressHook !== null) ; else {
// There are no remaining hooks from the previous attempt. We're no longer
// in "re-render" mode. Switch to the normal mount or update dispatcher.
//
// This is the same as the logic in renderWithHooks, except we don't bother
// to track the hook types debug information in this case (sufficient to
// only do that when nothing suspends).
var currentFiber = workInProgressFiber.alternate;
if (currentlyRenderingFiber$1.alternate === null && (workInProgressHook === null ? currentlyRenderingFiber$1.memoizedState === null : workInProgressHook.next === null)) {
// Initial render, and either this is the first time the component is
// called, or there were no Hooks called after this use() the previous
// time (perhaps because it threw). Subsequent Hook calls should use the
// mount dispatcher.
{
ReactSharedInternals.H = HooksDispatcherOnMountInDEV;
if (currentFiber !== null && currentFiber.memoizedState !== null) {
ReactSharedInternals.H = HooksDispatcherOnUpdateInDEV;
} else {
ReactSharedInternals.H = HooksDispatcherOnMountInDEV;
}
}
}
@@ -26562,7 +26586,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition
return root;
}
var ReactVersion = '19.0.0-rc-5c420e3824-20240531';
var ReactVersion = '19.0.0-rc-adbec0c25a-20240531';
/*
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<94353ed0f6f05f5f4d4ab74ba4240e98>>
* @generated SignedSource<<397a4e89bef38724e564db4d5df7d186>>
*/
"use strict";
@@ -3879,11 +3879,16 @@ function useThenable(thenable) {
thenableIndexCounter += 1;
null === thenableState && (thenableState = []);
thenable = trackUsedThenable(thenableState, thenable, index);
null === currentlyRenderingFiber$1.alternate &&
index = currentlyRenderingFiber$1;
null ===
(null === workInProgressHook
? null === currentlyRenderingFiber$1.memoizedState
: null === workInProgressHook.next) &&
(ReactSharedInternals.H = HooksDispatcherOnMount);
? index.memoizedState
: workInProgressHook.next) &&
((index = index.alternate),
(ReactSharedInternals.H =
null === index || null === index.memoizedState
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate));
return thenable;
}
function use(usable) {
@@ -10696,11 +10701,11 @@ function traverseOwnerTreeUp(hierarchy, instance) {
traverseOwnerTreeUp(hierarchy, instance);
}
var isomorphicReactPackageVersion = React.version;
if ("19.0.0-rc-5c420e3824-20240531" !== isomorphicReactPackageVersion)
if ("19.0.0-rc-adbec0c25a-20240531" !== isomorphicReactPackageVersion)
throw Error(
'Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got:\n - react: ' +
(isomorphicReactPackageVersion +
"\n - react-native-renderer: 19.0.0-rc-5c420e3824-20240531\nLearn more: https://react.dev/warnings/version-mismatch")
"\n - react-native-renderer: 19.0.0-rc-adbec0c25a-20240531\nLearn more: https://react.dev/warnings/version-mismatch")
);
if (
"function" !==
@@ -10750,7 +10755,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1192 = {
findFiberByHostInstance: getInstanceFromTag,
bundleType: 0,
version: "19.0.0-rc-5c420e3824-20240531",
version: "19.0.0-rc-adbec0c25a-20240531",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -10793,7 +10798,7 @@ var internals$jscomp$inline_1439 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-rc-5c420e3824-20240531"
reconcilerVersion: "19.0.0-rc-adbec0c25a-20240531"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1440 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<d986c946f72546291ea708de77c5caad>>
* @generated SignedSource<<75a5028640a4a5d0e637c6be72a8cf5a>>
*/
"use strict";
@@ -4001,11 +4001,16 @@ function useThenable(thenable) {
thenableIndexCounter += 1;
null === thenableState && (thenableState = []);
thenable = trackUsedThenable(thenableState, thenable, index);
null === currentlyRenderingFiber$1.alternate &&
index = currentlyRenderingFiber$1;
null ===
(null === workInProgressHook
? null === currentlyRenderingFiber$1.memoizedState
: null === workInProgressHook.next) &&
(ReactSharedInternals.H = HooksDispatcherOnMount);
? index.memoizedState
: workInProgressHook.next) &&
((index = index.alternate),
(ReactSharedInternals.H =
null === index || null === index.memoizedState
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate));
return thenable;
}
function use(usable) {
@@ -11403,11 +11408,11 @@ function traverseOwnerTreeUp(hierarchy, instance) {
traverseOwnerTreeUp(hierarchy, instance);
}
var isomorphicReactPackageVersion = React.version;
if ("19.0.0-rc-5c420e3824-20240531" !== isomorphicReactPackageVersion)
if ("19.0.0-rc-adbec0c25a-20240531" !== isomorphicReactPackageVersion)
throw Error(
'Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got:\n - react: ' +
(isomorphicReactPackageVersion +
"\n - react-native-renderer: 19.0.0-rc-5c420e3824-20240531\nLearn more: https://react.dev/warnings/version-mismatch")
"\n - react-native-renderer: 19.0.0-rc-adbec0c25a-20240531\nLearn more: https://react.dev/warnings/version-mismatch")
);
if (
"function" !==
@@ -11457,7 +11462,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1273 = {
findFiberByHostInstance: getInstanceFromTag,
bundleType: 0,
version: "19.0.0-rc-5c420e3824-20240531",
version: "19.0.0-rc-adbec0c25a-20240531",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -11513,7 +11518,7 @@ var roots = new Map(),
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-rc-5c420e3824-20240531"
reconcilerVersion: "19.0.0-rc-adbec0c25a-20240531"
});
exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
computeComponentStackForErrorReporting: function (reactTag) {