Implement experimental_useFormStatus (#26722)

This hook reads the status of its ancestor form component, if it exists.

```js
const {pending, data, action, method} = useFormStatus();
```

It can be used to implement a loading indicator, for example. You can
think of it as a shortcut for implementing a loading state with the
useTransition hook.

For now, it's only available in the experimental channel. We'll share
docs once its closer to being stable. There are additional APIs that
will ship alongside it.

Internally it's implemented using startTransition + a context object.
That's a good way to think about its behavior, but the actual
implementation details may change in the future.

Because form elements cannot be nested, the implementation in the
reconciler does not bother to keep track of multiple nested "transition
providers". So although it's implemented using generic Fiber config
methods, it does currently make some assumptions based on React DOM's
requirements.

DiffTrain build for commit https://github.com/facebook/react/commit/540bab085d571789f4562565eebfd0db9f36345c.
This commit is contained in:
acdlite
2023-04-26 22:24:49 +00:00
parent 2c417e24cc
commit fcc18f83c8
13 changed files with 96 additions and 105 deletions
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<1734297975a5c75886dc8f61c542873e>>
* @generated SignedSource<<147e07431dc4defee6d2e9faf584292a>>
*/
'use strict';
@@ -2587,7 +2587,7 @@ function isRootDehydrated(root) {
var contextStackCursor = createCursor(null);
var contextFiberStackCursor = createCursor(null);
var rootInstanceStackCursor = createCursor(null);
var rootInstanceStackCursor = createCursor(null); // Represents the nearest host transition provider (in React DOM, a <form />)
function requiredContext(c) {
{
@@ -2641,24 +2641,21 @@ function pushHostContext(fiber) {
var context = requiredContext(contextStackCursor.current);
var nextContext = getChildHostContext(); // Don't push this Fiber's context unless it's unique.
if (context === nextContext) {
return;
} // Track the context and the Fiber that provided it.
// This enables us to pop only Fibers that provide unique contexts.
push(contextFiberStackCursor, fiber, fiber);
push(contextStackCursor, nextContext, fiber);
if (context !== nextContext) {
// Track the context and the Fiber that provided it.
// This enables us to pop only Fibers that provide unique contexts.
push(contextFiberStackCursor, fiber, fiber);
push(contextStackCursor, nextContext, fiber);
}
}
function popHostContext(fiber) {
// Do not pop unless this Fiber provided the current context.
// pushHostContext() only pushes Fibers that provide unique contexts.
if (contextFiberStackCursor.current !== fiber) {
return;
if (contextFiberStackCursor.current === fiber) {
// Do not pop unless this Fiber provided the current context.
// pushHostContext() only pushes Fibers that provide unique contexts.
pop(contextStackCursor, fiber);
pop(contextFiberStackCursor, fiber);
}
pop(contextStackCursor, fiber);
pop(contextFiberStackCursor, fiber);
}
var isHydrating = false; // This flag allows for warning supression when we expect there to be mismatches
@@ -23895,7 +23892,7 @@ function createFiberRoot(
return root;
}
var ReactVersion = "18.3.0-next-6eadbe0c4-20230425";
var ReactVersion = "18.3.0-next-540bab085-20230426";
// Might add PROFILE later.
@@ -8601,7 +8601,7 @@ var devToolsConfig$jscomp$inline_1022 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "18.3.0-next-6eadbe0c4-20230425",
version: "18.3.0-next-540bab085-20230426",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1207 = {
@@ -8632,7 +8632,7 @@ var internals$jscomp$inline_1207 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-next-6eadbe0c4-20230425"
reconcilerVersion: "18.3.0-next-540bab085-20230426"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1208 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -9027,7 +9027,7 @@ var devToolsConfig$jscomp$inline_1064 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "18.3.0-next-6eadbe0c4-20230425",
version: "18.3.0-next-540bab085-20230426",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1248 = {
@@ -9058,7 +9058,7 @@ var internals$jscomp$inline_1248 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-next-6eadbe0c4-20230425"
reconcilerVersion: "18.3.0-next-540bab085-20230426"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1249 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -27,7 +27,7 @@ if (
}
"use strict";
var ReactVersion = "18.3.0-next-6eadbe0c4-20230425";
var ReactVersion = "18.3.0-next-540bab085-20230426";
// ATTENTION
// When adding new symbols to this file,
@@ -639,4 +639,4 @@ exports.useSyncExternalStore = function (
);
};
exports.useTransition = useTransition;
exports.version = "18.3.0-next-6eadbe0c4-20230425";
exports.version = "18.3.0-next-540bab085-20230426";
@@ -642,7 +642,7 @@ exports.useSyncExternalStore = function (
);
};
exports.useTransition = useTransition;
exports.version = "18.3.0-next-6eadbe0c4-20230425";
exports.version = "18.3.0-next-540bab085-20230426";
/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
@@ -1 +1 @@
6eadbe0c4aebf68410bb48147054ee22eec4c20c
540bab085d571789f4562565eebfd0db9f36345c
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<e06bc0ace400db16ad0c174d7641f083>>
* @generated SignedSource<<c56d233e78e730ff1d26a9cbc8974a45>>
*/
'use strict';
@@ -6268,7 +6268,7 @@ function isRootDehydrated(root) {
var contextStackCursor = createCursor(null);
var contextFiberStackCursor = createCursor(null);
var rootInstanceStackCursor = createCursor(null);
var rootInstanceStackCursor = createCursor(null); // Represents the nearest host transition provider (in React DOM, a <form />)
function requiredContext(c) {
{
@@ -6322,24 +6322,21 @@ function pushHostContext(fiber) {
var context = requiredContext(contextStackCursor.current);
var nextContext = getChildHostContext(context, fiber.type); // Don't push this Fiber's context unless it's unique.
if (context === nextContext) {
return;
} // Track the context and the Fiber that provided it.
// This enables us to pop only Fibers that provide unique contexts.
push(contextFiberStackCursor, fiber, fiber);
push(contextStackCursor, nextContext, fiber);
if (context !== nextContext) {
// Track the context and the Fiber that provided it.
// This enables us to pop only Fibers that provide unique contexts.
push(contextFiberStackCursor, fiber, fiber);
push(contextStackCursor, nextContext, fiber);
}
}
function popHostContext(fiber) {
// Do not pop unless this Fiber provided the current context.
// pushHostContext() only pushes Fibers that provide unique contexts.
if (contextFiberStackCursor.current !== fiber) {
return;
if (contextFiberStackCursor.current === fiber) {
// Do not pop unless this Fiber provided the current context.
// pushHostContext() only pushes Fibers that provide unique contexts.
pop(contextStackCursor, fiber);
pop(contextFiberStackCursor, fiber);
}
pop(contextStackCursor, fiber);
pop(contextFiberStackCursor, fiber);
}
var isHydrating = false; // This flag allows for warning supression when we expect there to be mismatches
@@ -11376,11 +11373,11 @@ function useMutableSource(hook, source, getSnapshot, subscribe) {
var version = getVersion(source._source);
var dispatcher = ReactCurrentDispatcher$1.current; // eslint-disable-next-line prefer-const
var _dispatcher$useState = dispatcher.useState(function () {
var _dispatcher$useState2 = dispatcher.useState(function () {
return readFromUnsubscribedMutableSource(root, source, getSnapshot);
}),
currentSnapshot = _dispatcher$useState[0],
setSnapshot = _dispatcher$useState[1];
currentSnapshot = _dispatcher$useState2[0],
setSnapshot = _dispatcher$useState2[1];
var snapshot = currentSnapshot; // Grab a handle to the state hook as well.
// We use it to clear the pending update queue if we have a new source.
@@ -27205,7 +27202,7 @@ function createFiberRoot(
return root;
}
var ReactVersion = "18.3.0-next-6eadbe0c4-20230425";
var ReactVersion = "18.3.0-next-540bab085-20230426";
function createPortal$1(
children,
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<ebafb0bec3eaf52ea1b8517bbab46593>>
* @generated SignedSource<<eea5199d8ea5e7e01ee9bf40f6eb4713>>
*/
"use strict";
@@ -3869,12 +3869,12 @@ function useMutableSource(hook, source, getSnapshot, subscribe) {
var getVersion = source._getVersion,
version = getVersion(source._source),
dispatcher = ReactCurrentDispatcher$1.current,
_dispatcher$useState = dispatcher.useState(function () {
_dispatcher$useState2 = dispatcher.useState(function () {
return readFromUnsubscribedMutableSource(root, source, getSnapshot);
}),
setSnapshot = _dispatcher$useState[1],
snapshot = _dispatcher$useState[0];
_dispatcher$useState = workInProgressHook;
setSnapshot = _dispatcher$useState2[1],
snapshot = _dispatcher$useState2[0];
_dispatcher$useState2 = workInProgressHook;
var memoizedState = hook.memoizedState,
refs = memoizedState.refs,
prevGetSnapshot = refs.getSnapshot,
@@ -3927,10 +3927,10 @@ function useMutableSource(hook, source, getSnapshot, subscribe) {
}),
(hook.dispatch = setSnapshot =
dispatchSetState.bind(null, currentlyRenderingFiber$1, hook)),
(_dispatcher$useState.queue = hook),
(_dispatcher$useState.baseQueue = null),
(_dispatcher$useState2.queue = hook),
(_dispatcher$useState2.baseQueue = null),
(snapshot = readFromUnsubscribedMutableSource(root, source, getSnapshot)),
(_dispatcher$useState.memoizedState = _dispatcher$useState.baseState =
(_dispatcher$useState2.memoizedState = _dispatcher$useState2.baseState =
snapshot));
return snapshot;
}
@@ -9472,7 +9472,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1046 = {
findFiberByHostInstance: getInstanceFromNode,
bundleType: 0,
version: "18.3.0-next-6eadbe0c4-20230425",
version: "18.3.0-next-540bab085-20230426",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForViewTag: function () {
@@ -9514,7 +9514,7 @@ var internals$jscomp$inline_1277 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-next-6eadbe0c4-20230425"
reconcilerVersion: "18.3.0-next-540bab085-20230426"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1278 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<c2ea9a9314e4d1947b80adea64e6d9ce>>
* @generated SignedSource<<7634b64643555a5e869a82fd5e1dc828>>
*/
@@ -3999,12 +3999,12 @@ function useMutableSource(hook, source, getSnapshot, subscribe) {
var getVersion = source._getVersion,
version = getVersion(source._source),
dispatcher = ReactCurrentDispatcher$1.current,
_dispatcher$useState = dispatcher.useState(function () {
_dispatcher$useState2 = dispatcher.useState(function () {
return readFromUnsubscribedMutableSource(root, source, getSnapshot);
}),
setSnapshot = _dispatcher$useState[1],
snapshot = _dispatcher$useState[0];
_dispatcher$useState = workInProgressHook;
setSnapshot = _dispatcher$useState2[1],
snapshot = _dispatcher$useState2[0];
_dispatcher$useState2 = workInProgressHook;
var memoizedState = hook.memoizedState,
refs = memoizedState.refs,
prevGetSnapshot = refs.getSnapshot,
@@ -4057,10 +4057,10 @@ function useMutableSource(hook, source, getSnapshot, subscribe) {
}),
(hook.dispatch = setSnapshot =
dispatchSetState.bind(null, currentlyRenderingFiber$1, hook)),
(_dispatcher$useState.queue = hook),
(_dispatcher$useState.baseQueue = null),
(_dispatcher$useState2.queue = hook),
(_dispatcher$useState2.baseQueue = null),
(snapshot = readFromUnsubscribedMutableSource(root, source, getSnapshot)),
(_dispatcher$useState.memoizedState = _dispatcher$useState.baseState =
(_dispatcher$useState2.memoizedState = _dispatcher$useState2.baseState =
snapshot));
return snapshot;
}
@@ -10181,7 +10181,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1124 = {
findFiberByHostInstance: getInstanceFromNode,
bundleType: 0,
version: "18.3.0-next-6eadbe0c4-20230425",
version: "18.3.0-next-540bab085-20230426",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForViewTag: function () {
@@ -10236,7 +10236,7 @@ var roots = new Map(),
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-next-6eadbe0c4-20230425"
reconcilerVersion: "18.3.0-next-540bab085-20230426"
});
exports.createPortal = function (children, containerTag) {
return createPortal$1(
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<812e354f66e1eb672accd40316666e44>>
* @generated SignedSource<<22b18563a8650deb3b64f41ff6b2aec7>>
*/
'use strict';
@@ -6584,7 +6584,7 @@ function isRootDehydrated(root) {
var contextStackCursor = createCursor(null);
var contextFiberStackCursor = createCursor(null);
var rootInstanceStackCursor = createCursor(null);
var rootInstanceStackCursor = createCursor(null); // Represents the nearest host transition provider (in React DOM, a <form />)
function requiredContext(c) {
{
@@ -6638,24 +6638,21 @@ function pushHostContext(fiber) {
var context = requiredContext(contextStackCursor.current);
var nextContext = getChildHostContext(context, fiber.type); // Don't push this Fiber's context unless it's unique.
if (context === nextContext) {
return;
} // Track the context and the Fiber that provided it.
// This enables us to pop only Fibers that provide unique contexts.
push(contextFiberStackCursor, fiber, fiber);
push(contextStackCursor, nextContext, fiber);
if (context !== nextContext) {
// Track the context and the Fiber that provided it.
// This enables us to pop only Fibers that provide unique contexts.
push(contextFiberStackCursor, fiber, fiber);
push(contextStackCursor, nextContext, fiber);
}
}
function popHostContext(fiber) {
// Do not pop unless this Fiber provided the current context.
// pushHostContext() only pushes Fibers that provide unique contexts.
if (contextFiberStackCursor.current !== fiber) {
return;
if (contextFiberStackCursor.current === fiber) {
// Do not pop unless this Fiber provided the current context.
// pushHostContext() only pushes Fibers that provide unique contexts.
pop(contextStackCursor, fiber);
pop(contextFiberStackCursor, fiber);
}
pop(contextStackCursor, fiber);
pop(contextFiberStackCursor, fiber);
}
var isHydrating = false; // This flag allows for warning supression when we expect there to be mismatches
@@ -11692,11 +11689,11 @@ function useMutableSource(hook, source, getSnapshot, subscribe) {
var version = getVersion(source._source);
var dispatcher = ReactCurrentDispatcher$1.current; // eslint-disable-next-line prefer-const
var _dispatcher$useState = dispatcher.useState(function () {
var _dispatcher$useState2 = dispatcher.useState(function () {
return readFromUnsubscribedMutableSource(root, source, getSnapshot);
}),
currentSnapshot = _dispatcher$useState[0],
setSnapshot = _dispatcher$useState[1];
currentSnapshot = _dispatcher$useState2[0],
setSnapshot = _dispatcher$useState2[1];
var snapshot = currentSnapshot; // Grab a handle to the state hook as well.
// We use it to clear the pending update queue if we have a new source.
@@ -27718,7 +27715,7 @@ function createFiberRoot(
return root;
}
var ReactVersion = "18.3.0-next-6eadbe0c4-20230425";
var ReactVersion = "18.3.0-next-540bab085-20230426";
function createPortal$1(
children,
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<c6ebdbbb2fc107aff4f73b8e4e0d0219>>
* @generated SignedSource<<af3a4dae6a45a4183ba8d894550b2310>>
*/
"use strict";
@@ -3959,12 +3959,12 @@ function useMutableSource(hook, source, getSnapshot, subscribe) {
var getVersion = source._getVersion,
version = getVersion(source._source),
dispatcher = ReactCurrentDispatcher$1.current,
_dispatcher$useState = dispatcher.useState(function () {
_dispatcher$useState2 = dispatcher.useState(function () {
return readFromUnsubscribedMutableSource(root, source, getSnapshot);
}),
setSnapshot = _dispatcher$useState[1],
snapshot = _dispatcher$useState[0];
_dispatcher$useState = workInProgressHook;
setSnapshot = _dispatcher$useState2[1],
snapshot = _dispatcher$useState2[0];
_dispatcher$useState2 = workInProgressHook;
var memoizedState = hook.memoizedState,
refs = memoizedState.refs,
prevGetSnapshot = refs.getSnapshot,
@@ -4017,10 +4017,10 @@ function useMutableSource(hook, source, getSnapshot, subscribe) {
}),
(hook.dispatch = setSnapshot =
dispatchSetState.bind(null, currentlyRenderingFiber$1, hook)),
(_dispatcher$useState.queue = hook),
(_dispatcher$useState.baseQueue = null),
(_dispatcher$useState2.queue = hook),
(_dispatcher$useState2.baseQueue = null),
(snapshot = readFromUnsubscribedMutableSource(root, source, getSnapshot)),
(_dispatcher$useState.memoizedState = _dispatcher$useState.baseState =
(_dispatcher$useState2.memoizedState = _dispatcher$useState2.baseState =
snapshot));
return snapshot;
}
@@ -9731,7 +9731,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1101 = {
findFiberByHostInstance: getInstanceFromTag,
bundleType: 0,
version: "18.3.0-next-6eadbe0c4-20230425",
version: "18.3.0-next-540bab085-20230426",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForViewTag: function () {
@@ -9773,7 +9773,7 @@ var internals$jscomp$inline_1346 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-next-6eadbe0c4-20230425"
reconcilerVersion: "18.3.0-next-540bab085-20230426"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1347 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<a18b7b7f49bb2bff66653fab20ea5e59>>
* @generated SignedSource<<96114abf645049ee264d3195be710b11>>
*/
@@ -4089,12 +4089,12 @@ function useMutableSource(hook, source, getSnapshot, subscribe) {
var getVersion = source._getVersion,
version = getVersion(source._source),
dispatcher = ReactCurrentDispatcher$1.current,
_dispatcher$useState = dispatcher.useState(function () {
_dispatcher$useState2 = dispatcher.useState(function () {
return readFromUnsubscribedMutableSource(root, source, getSnapshot);
}),
setSnapshot = _dispatcher$useState[1],
snapshot = _dispatcher$useState[0];
_dispatcher$useState = workInProgressHook;
setSnapshot = _dispatcher$useState2[1],
snapshot = _dispatcher$useState2[0];
_dispatcher$useState2 = workInProgressHook;
var memoizedState = hook.memoizedState,
refs = memoizedState.refs,
prevGetSnapshot = refs.getSnapshot,
@@ -4147,10 +4147,10 @@ function useMutableSource(hook, source, getSnapshot, subscribe) {
}),
(hook.dispatch = setSnapshot =
dispatchSetState.bind(null, currentlyRenderingFiber$1, hook)),
(_dispatcher$useState.queue = hook),
(_dispatcher$useState.baseQueue = null),
(_dispatcher$useState2.queue = hook),
(_dispatcher$useState2.baseQueue = null),
(snapshot = readFromUnsubscribedMutableSource(root, source, getSnapshot)),
(_dispatcher$useState.memoizedState = _dispatcher$useState.baseState =
(_dispatcher$useState2.memoizedState = _dispatcher$useState2.baseState =
snapshot));
return snapshot;
}
@@ -10440,7 +10440,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1179 = {
findFiberByHostInstance: getInstanceFromTag,
bundleType: 0,
version: "18.3.0-next-6eadbe0c4-20230425",
version: "18.3.0-next-540bab085-20230426",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForViewTag: function () {
@@ -10495,7 +10495,7 @@ var roots = new Map(),
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-next-6eadbe0c4-20230425"
reconcilerVersion: "18.3.0-next-540bab085-20230426"
});
exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
computeComponentStackForErrorReporting: function (reactTag) {