Skip to content

Commit

Permalink
useFormState: Reuse state from previous form submission (#27321)
Browse files Browse the repository at this point in the history
If a Server Action is passed to useFormState, the action may be
submitted before it has hydrated. This will trigger a full page
(MPA-style) navigation. We can transfer the form state to the next page
by comparing the key path of the hook instance.

`ReactServerDOMServer.decodeFormState` is used by the server to extract
the form state from the submitted action. This value can then be passed
as an option when rendering the new page. It must be passed during both
SSR and hydration.

```js
const boundAction = await decodeAction(formData, serverManifest);
const result = await boundAction();
const formState = decodeFormState(result, formData, serverManifest);

// SSR
const response = createFromReadableStream(<App />);
const ssrStream = await renderToReadableStream(response, { formState })

// Hydration
hydrateRoot(container, <App />, { formState });
```

If the `formState` option is omitted, then the state won't be
transferred to the next page. However, it must be passed in both places,
or in neither; misconfiguring will result in a hydration mismatch.

(The `formState` option is currently prefixed with `experimental_`)

DiffTrain build for commit 612b2b6.
  • Loading branch information
acdlite committed Sep 13, 2023
1 parent 419e64c commit fc5a146
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<cd445f3b30b95bc14ba23cb28c191840>>
* @generated SignedSource<<90d4b49133c0f3de327f45d0812fd8e9>>
*/

'use strict';
Expand Down Expand Up @@ -23883,7 +23883,8 @@ function FiberRootNode(
tag,
hydrate,
identifierPrefix,
onRecoverableError
onRecoverableError,
formState
) {
this.tag = tag;
this.containerInfo = containerInfo;
Expand Down Expand Up @@ -23917,6 +23918,7 @@ function FiberRootNode(
this.pooledCacheLanes = NoLanes;
}

this.formState = formState;
this.incompleteTransitions = new Map();

{
Expand Down Expand Up @@ -23950,15 +23952,17 @@ function createFiberRoot(
// single type, like a DynamicHostConfig that is defined by the renderer.
identifierPrefix,
onRecoverableError,
transitionCallbacks
transitionCallbacks,
formState
) {
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
var root = new FiberRootNode(
containerInfo,
tag,
hydrate,
identifierPrefix,
onRecoverableError
onRecoverableError,
formState
);
// stateNode is any.

Expand Down Expand Up @@ -23994,7 +23998,7 @@ function createFiberRoot(
return root;
}

var ReactVersion = "18.3.0-canary-e5205658f-20230913";
var ReactVersion = "18.3.0-canary-612b2b660-20230913";

// Might add PROFILE later.

Expand Down Expand Up @@ -24044,7 +24048,9 @@ function createContainer(
isStrictMode,
concurrentUpdatesByDefaultOverride,
identifierPrefix,
onRecoverableError
onRecoverableError,
transitionCallbacks,
null
);
}
function updateContainer(element, container, parentComponent, callback) {
Expand Down Expand Up @@ -24911,7 +24917,8 @@ function create(element, options) {
isStrictMode,
concurrentUpdatesByDefault,
"",
onRecoverableError
onRecoverableError,
null
);

if (root == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<d1b32810e51fc98372bfbb5c93420bef>>
* @generated SignedSource<<5361a5f040398d7d166b0f13b30a010c>>
*/

"use strict";
Expand Down Expand Up @@ -8179,7 +8179,8 @@ function FiberRootNode(
tag,
hydrate,
identifierPrefix,
onRecoverableError
onRecoverableError,
formState
) {
this.tag = tag;
this.containerInfo = containerInfo;
Expand Down Expand Up @@ -8212,6 +8213,7 @@ function FiberRootNode(
this.onRecoverableError = onRecoverableError;
this.pooledCache = null;
this.pooledCacheLanes = 0;
this.formState = formState;
this.incompleteTransitions = new Map();
}
function createContainer(
Expand All @@ -8228,7 +8230,8 @@ function createContainer(
tag,
!1,
identifierPrefix,
onRecoverableError
onRecoverableError,
null
);
1 === tag
? ((tag = 1),
Expand Down Expand Up @@ -8612,19 +8615,19 @@ function wrapFiber(fiber) {
fiberToWrapper.set(fiber, wrapper));
return wrapper;
}
var devToolsConfig$jscomp$inline_1027 = {
var devToolsConfig$jscomp$inline_1028 = {
findFiberByHostInstance: function () {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "18.3.0-canary-e5205658f-20230913",
version: "18.3.0-canary-612b2b660-20230913",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1226 = {
bundleType: devToolsConfig$jscomp$inline_1027.bundleType,
version: devToolsConfig$jscomp$inline_1027.version,
rendererPackageName: devToolsConfig$jscomp$inline_1027.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1027.rendererConfig,
var internals$jscomp$inline_1227 = {
bundleType: devToolsConfig$jscomp$inline_1028.bundleType,
version: devToolsConfig$jscomp$inline_1028.version,
rendererPackageName: devToolsConfig$jscomp$inline_1028.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1028.rendererConfig,
overrideHookState: null,
overrideHookStateDeletePath: null,
overrideHookStateRenamePath: null,
Expand All @@ -8641,26 +8644,26 @@ var internals$jscomp$inline_1226 = {
return null === fiber ? null : fiber.stateNode;
},
findFiberByHostInstance:
devToolsConfig$jscomp$inline_1027.findFiberByHostInstance ||
devToolsConfig$jscomp$inline_1028.findFiberByHostInstance ||
emptyFindFiberByHostInstance,
findHostInstancesForRefresh: null,
scheduleRefresh: null,
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-canary-e5205658f-20230913"
reconcilerVersion: "18.3.0-canary-612b2b660-20230913"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1227 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
var hook$jscomp$inline_1228 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
if (
!hook$jscomp$inline_1227.isDisabled &&
hook$jscomp$inline_1227.supportsFiber
!hook$jscomp$inline_1228.isDisabled &&
hook$jscomp$inline_1228.supportsFiber
)
try {
(rendererID = hook$jscomp$inline_1227.inject(
internals$jscomp$inline_1226
(rendererID = hook$jscomp$inline_1228.inject(
internals$jscomp$inline_1227
)),
(injectedHook = hook$jscomp$inline_1227);
(injectedHook = hook$jscomp$inline_1228);
} catch (err) {}
}
exports._Scheduler = Scheduler;
Expand Down Expand Up @@ -8691,7 +8694,8 @@ exports.create = function (element, options) {
isStrictMode,
concurrentUpdatesByDefault,
"",
onRecoverableError
onRecoverableError,
null
);
if (null == root) throw Error("something went wrong");
updateContainer(element, root, null, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<c0a54e40c38caf663b646a4c7f7321d1>>
* @generated SignedSource<<486546c05387fd5f0f9d0c9c12aa9b75>>
*/

"use strict";
Expand Down Expand Up @@ -8603,7 +8603,8 @@ function FiberRootNode(
tag,
hydrate,
identifierPrefix,
onRecoverableError
onRecoverableError,
formState
) {
this.tag = tag;
this.containerInfo = containerInfo;
Expand Down Expand Up @@ -8636,6 +8637,7 @@ function FiberRootNode(
this.onRecoverableError = onRecoverableError;
this.pooledCache = null;
this.pooledCacheLanes = 0;
this.formState = formState;
this.incompleteTransitions = new Map();
this.passiveEffectDuration = this.effectDuration = 0;
}
Expand All @@ -8653,7 +8655,8 @@ function createContainer(
tag,
!1,
identifierPrefix,
onRecoverableError
onRecoverableError,
null
);
1 === tag
? ((tag = 1),
Expand Down Expand Up @@ -9038,19 +9041,19 @@ function wrapFiber(fiber) {
fiberToWrapper.set(fiber, wrapper));
return wrapper;
}
var devToolsConfig$jscomp$inline_1069 = {
var devToolsConfig$jscomp$inline_1070 = {
findFiberByHostInstance: function () {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "18.3.0-canary-e5205658f-20230913",
version: "18.3.0-canary-612b2b660-20230913",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1267 = {
bundleType: devToolsConfig$jscomp$inline_1069.bundleType,
version: devToolsConfig$jscomp$inline_1069.version,
rendererPackageName: devToolsConfig$jscomp$inline_1069.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1069.rendererConfig,
var internals$jscomp$inline_1268 = {
bundleType: devToolsConfig$jscomp$inline_1070.bundleType,
version: devToolsConfig$jscomp$inline_1070.version,
rendererPackageName: devToolsConfig$jscomp$inline_1070.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1070.rendererConfig,
overrideHookState: null,
overrideHookStateDeletePath: null,
overrideHookStateRenamePath: null,
Expand All @@ -9067,26 +9070,26 @@ var internals$jscomp$inline_1267 = {
return null === fiber ? null : fiber.stateNode;
},
findFiberByHostInstance:
devToolsConfig$jscomp$inline_1069.findFiberByHostInstance ||
devToolsConfig$jscomp$inline_1070.findFiberByHostInstance ||
emptyFindFiberByHostInstance,
findHostInstancesForRefresh: null,
scheduleRefresh: null,
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-canary-e5205658f-20230913"
reconcilerVersion: "18.3.0-canary-612b2b660-20230913"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1268 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
var hook$jscomp$inline_1269 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
if (
!hook$jscomp$inline_1268.isDisabled &&
hook$jscomp$inline_1268.supportsFiber
!hook$jscomp$inline_1269.isDisabled &&
hook$jscomp$inline_1269.supportsFiber
)
try {
(rendererID = hook$jscomp$inline_1268.inject(
internals$jscomp$inline_1267
(rendererID = hook$jscomp$inline_1269.inject(
internals$jscomp$inline_1268
)),
(injectedHook = hook$jscomp$inline_1268);
(injectedHook = hook$jscomp$inline_1269);
} catch (err) {}
}
exports._Scheduler = Scheduler;
Expand Down Expand Up @@ -9117,7 +9120,8 @@ exports.create = function (element, options) {
isStrictMode,
concurrentUpdatesByDefault,
"",
onRecoverableError
onRecoverableError,
null
);
if (null == root) throw Error("something went wrong");
updateContainer(element, root, null, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ if (
}
"use strict";

var ReactVersion = "18.3.0-canary-e5205658f-20230913";
var ReactVersion = "18.3.0-canary-612b2b660-20230913";

// ATTENTION
// When adding new symbols to this file,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,4 +616,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactCurrentDispatcher.current.useTransition();
};
exports.version = "18.3.0-canary-e5205658f-20230913";
exports.version = "18.3.0-canary-612b2b660-20230913";
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactCurrentDispatcher.current.useTransition();
};
exports.version = "18.3.0-canary-e5205658f-20230913";
exports.version = "18.3.0-canary-612b2b660-20230913";

/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
e5205658f40ad181279857dbb66e36b8ebcd8c0e
612b2b6601abb844248c384d1e288bb824b180b7
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<588595e62718a973abbe3ee5129ccaa8>>
* @generated SignedSource<<01008a8950368c139ee32158a8728c5d>>
*/

'use strict';
Expand Down Expand Up @@ -26929,7 +26929,8 @@ function FiberRootNode(
tag,
hydrate,
identifierPrefix,
onRecoverableError
onRecoverableError,
formState
) {
this.tag = tag;
this.containerInfo = containerInfo;
Expand Down Expand Up @@ -26958,6 +26959,7 @@ function FiberRootNode(
this.identifierPrefix = identifierPrefix;
this.onRecoverableError = onRecoverableError;

this.formState = formState;
this.incompleteTransitions = new Map();

{
Expand Down Expand Up @@ -27000,15 +27002,17 @@ function createFiberRoot(
// single type, like a DynamicHostConfig that is defined by the renderer.
identifierPrefix,
onRecoverableError,
transitionCallbacks
transitionCallbacks,
formState
) {
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
var root = new FiberRootNode(
containerInfo,
tag,
hydrate,
identifierPrefix,
onRecoverableError
onRecoverableError,
formState
);
// stateNode is any.

Expand All @@ -27033,7 +27037,7 @@ function createFiberRoot(
return root;
}

var ReactVersion = "18.3.0-canary-8ee3c01f";
var ReactVersion = "18.3.0-canary-f73f8a34";

function createPortal$1(
children,
Expand Down Expand Up @@ -27177,7 +27181,9 @@ function createContainer(
isStrictMode,
concurrentUpdatesByDefaultOverride,
identifierPrefix,
onRecoverableError
onRecoverableError,
transitionCallbacks,
null
);
}
function updateContainer(element, container, parentComponent, callback) {
Expand Down Expand Up @@ -28005,7 +28011,8 @@ function render(element, containerTag, callback, concurrentRoot) {
false,
null,
"",
onRecoverableError
onRecoverableError,
null
);
roots.set(containerTag, root);
}
Expand Down
Loading

0 comments on commit fc5a146

Please sign in to comment.