From dd401dc3e8aeeaaaf26b16c588e7cb2d6da13f8d Mon Sep 17 00:00:00 2001 From: acdlite Date: Tue, 29 Aug 2023 16:03:42 +0000 Subject: [PATCH] useFormState's permalink option changes form target (#27302) When the `permalink` option is passed to `useFormState`, and the form is submitted before it has hydrated, the permalink will be used as the target of the form action, enabling MPA-style form submissions. (Note that submitting a form without hydration is a feature of Server Actions; it doesn't work with regular client actions.) It does not have any effect after the form has hydrated. DiffTrain build for [ddff504695f33c19e8c0792bff82bd8f8b8f7c05](https://github.com/facebook/react/commit/ddff504695f33c19e8c0792bff82bd8f8b8f7c05) --- compiled/facebook-www/REVISION | 2 +- compiled/facebook-www/ReactART-dev.modern.js | 2 +- compiled/facebook-www/ReactDOM-dev.classic.js | 4 +- compiled/facebook-www/ReactDOM-dev.modern.js | 4 +- .../ReactDOMServer-dev.classic.js | 39 +++++++++++++++---- .../facebook-www/ReactDOMServer-dev.modern.js | 39 +++++++++++++++---- .../ReactDOMServer-prod.classic.js | 19 ++++++--- .../ReactDOMServer-prod.modern.js | 19 ++++++--- .../ReactDOMServerStreaming-dev.modern.js | 37 ++++++++++++++---- .../ReactDOMServerStreaming-prod.modern.js | 17 +++++--- .../ReactDOMTesting-dev.classic.js | 4 +- .../ReactDOMTesting-dev.modern.js | 4 +- .../ReactTestRenderer-dev.classic.js | 2 +- 13 files changed, 141 insertions(+), 51 deletions(-) diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index 8b49665429..2867073493 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -eaa696876ee40bb048727aefe995be1bbb7384a8 +ddff504695f33c19e8c0792bff82bd8f8b8f7c05 diff --git a/compiled/facebook-www/ReactART-dev.modern.js b/compiled/facebook-www/ReactART-dev.modern.js index cce5b3fb66..9dc3f5be33 100644 --- a/compiled/facebook-www/ReactART-dev.modern.js +++ b/compiled/facebook-www/ReactART-dev.modern.js @@ -69,7 +69,7 @@ function _assertThisInitialized(self) { return self; } -var ReactVersion = "18.3.0-www-modern-fa00f381"; +var ReactVersion = "18.3.0-www-modern-90201c21"; var LegacyRoot = 0; var ConcurrentRoot = 1; diff --git a/compiled/facebook-www/ReactDOM-dev.classic.js b/compiled/facebook-www/ReactDOM-dev.classic.js index 9965519948..1559bfbd38 100644 --- a/compiled/facebook-www/ReactDOM-dev.classic.js +++ b/compiled/facebook-www/ReactDOM-dev.classic.js @@ -983,7 +983,7 @@ function useFormStatus() { throw new Error("Not implemented."); } } -function useFormState(action, initialState, url) { +function useFormState(action, initialState, permalink) { { throw new Error("Not implemented."); } @@ -34008,7 +34008,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-d4ec9077"; +var ReactVersion = "18.3.0-www-classic-4045cb9c"; function createPortal$1( children, diff --git a/compiled/facebook-www/ReactDOM-dev.modern.js b/compiled/facebook-www/ReactDOM-dev.modern.js index 447ec084d9..3db46076ac 100644 --- a/compiled/facebook-www/ReactDOM-dev.modern.js +++ b/compiled/facebook-www/ReactDOM-dev.modern.js @@ -155,7 +155,7 @@ function useFormStatus() { throw new Error("Not implemented."); } } -function useFormState(action, initialState, url) { +function useFormState(action, initialState, permalink) { { throw new Error("Not implemented."); } @@ -33853,7 +33853,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-modern-fa00f381"; +var ReactVersion = "18.3.0-www-modern-90201c21"; function createPortal$1( children, diff --git a/compiled/facebook-www/ReactDOMServer-dev.classic.js b/compiled/facebook-www/ReactDOMServer-dev.classic.js index 4f269a415d..5fa260170f 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.classic.js +++ b/compiled/facebook-www/ReactDOMServer-dev.classic.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-classic-dbbf9e79"; +var ReactVersion = "18.3.0-www-classic-0ebcd846"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -9596,18 +9596,41 @@ function unsupportedSetOptimisticState() { throw new Error("Cannot update optimistic state while rendering."); } -function unsupportedDispatchFormState() { - throw new Error("Cannot update form state while rendering."); -} - function useOptimistic(passthrough, reducer) { resolveCurrentlyRenderingComponent(); return [passthrough, unsupportedSetOptimisticState]; } -function useFormState(action, initialState, url) { - resolveCurrentlyRenderingComponent(); - return [initialState, unsupportedDispatchFormState]; +function useFormState(action, initialState, permalink) { + resolveCurrentlyRenderingComponent(); // Bind the initial state to the first argument of the action. + // TODO: Use the keypath (or permalink) to check if there's matching state + // from the previous page. + + var boundAction = action.bind(null, initialState); // Wrap the action so the return value is void. + + var dispatch = function (payload) { + boundAction(payload); + }; // $FlowIgnore[prop-missing] + + if (typeof boundAction.$$FORM_ACTION === "function") { + // $FlowIgnore[prop-missing] + dispatch.$$FORM_ACTION = function (prefix) { + // $FlowIgnore[prop-missing] + var metadata = boundAction.$$FORM_ACTION(prefix); // Override the target URL + + if (permalink !== undefined) { + { + checkAttributeStringCoercion(permalink, "target"); + } + + metadata.target = permalink + ""; + } + + return metadata; + }; + } + + return [initialState, dispatch]; } function useId() { diff --git a/compiled/facebook-www/ReactDOMServer-dev.modern.js b/compiled/facebook-www/ReactDOMServer-dev.modern.js index ad2b879f02..65424b8cf6 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServer-dev.modern.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-modern-451e1736"; +var ReactVersion = "18.3.0-www-modern-528c5df7"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -9355,18 +9355,41 @@ function unsupportedSetOptimisticState() { throw new Error("Cannot update optimistic state while rendering."); } -function unsupportedDispatchFormState() { - throw new Error("Cannot update form state while rendering."); -} - function useOptimistic(passthrough, reducer) { resolveCurrentlyRenderingComponent(); return [passthrough, unsupportedSetOptimisticState]; } -function useFormState(action, initialState, url) { - resolveCurrentlyRenderingComponent(); - return [initialState, unsupportedDispatchFormState]; +function useFormState(action, initialState, permalink) { + resolveCurrentlyRenderingComponent(); // Bind the initial state to the first argument of the action. + // TODO: Use the keypath (or permalink) to check if there's matching state + // from the previous page. + + var boundAction = action.bind(null, initialState); // Wrap the action so the return value is void. + + var dispatch = function (payload) { + boundAction(payload); + }; // $FlowIgnore[prop-missing] + + if (typeof boundAction.$$FORM_ACTION === "function") { + // $FlowIgnore[prop-missing] + dispatch.$$FORM_ACTION = function (prefix) { + // $FlowIgnore[prop-missing] + var metadata = boundAction.$$FORM_ACTION(prefix); // Override the target URL + + if (permalink !== undefined) { + { + checkAttributeStringCoercion(permalink, "target"); + } + + metadata.target = permalink + ""; + } + + return metadata; + }; + } + + return [initialState, dispatch]; } function useId() { diff --git a/compiled/facebook-www/ReactDOMServer-prod.classic.js b/compiled/facebook-www/ReactDOMServer-prod.classic.js index 7e554b2a81..34d66a276f 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.classic.js +++ b/compiled/facebook-www/ReactDOMServer-prod.classic.js @@ -2773,16 +2773,23 @@ function unsupportedStartTransition() { function unsupportedSetOptimisticState() { throw Error(formatProdErrorMessage(479)); } -function unsupportedDispatchFormState() { - throw Error(formatProdErrorMessage(485)); -} function useOptimistic(passthrough) { resolveCurrentlyRenderingComponent(); return [passthrough, unsupportedSetOptimisticState]; } -function useFormState(action, initialState) { +function useFormState(action, initialState, permalink) { + function dispatch(payload) { + boundAction(payload); + } resolveCurrentlyRenderingComponent(); - return [initialState, unsupportedDispatchFormState]; + var boundAction = action.bind(null, initialState); + "function" === typeof boundAction.$$FORM_ACTION && + (dispatch.$$FORM_ACTION = function (prefix) { + prefix = boundAction.$$FORM_ACTION(prefix); + void 0 !== permalink && (prefix.target = permalink + ""); + return prefix; + }); + return [initialState, dispatch]; } function unwrapThenable(thenable) { var index = thenableIndexCounter; @@ -4408,4 +4415,4 @@ exports.renderToString = function (children, options) { 'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server' ); }; -exports.version = "18.3.0-www-classic-7f028090"; +exports.version = "18.3.0-www-classic-31e5c37c"; diff --git a/compiled/facebook-www/ReactDOMServer-prod.modern.js b/compiled/facebook-www/ReactDOMServer-prod.modern.js index 31d34a1f7f..a67f6ca1d0 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.modern.js +++ b/compiled/facebook-www/ReactDOMServer-prod.modern.js @@ -2765,16 +2765,23 @@ function unsupportedStartTransition() { function unsupportedSetOptimisticState() { throw Error(formatProdErrorMessage(479)); } -function unsupportedDispatchFormState() { - throw Error(formatProdErrorMessage(485)); -} function useOptimistic(passthrough) { resolveCurrentlyRenderingComponent(); return [passthrough, unsupportedSetOptimisticState]; } -function useFormState(action, initialState) { +function useFormState(action, initialState, permalink) { + function dispatch(payload) { + boundAction(payload); + } resolveCurrentlyRenderingComponent(); - return [initialState, unsupportedDispatchFormState]; + var boundAction = action.bind(null, initialState); + "function" === typeof boundAction.$$FORM_ACTION && + (dispatch.$$FORM_ACTION = function (prefix) { + prefix = boundAction.$$FORM_ACTION(prefix); + void 0 !== permalink && (prefix.target = permalink + ""); + return prefix; + }); + return [initialState, dispatch]; } function unwrapThenable(thenable) { var index = thenableIndexCounter; @@ -4391,4 +4398,4 @@ exports.renderToString = function (children, options) { 'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server' ); }; -exports.version = "18.3.0-www-modern-972e36e6"; +exports.version = "18.3.0-www-modern-03f0f7fc"; diff --git a/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js b/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js index 3918bd32b2..a8ea0bb20f 100644 --- a/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js @@ -9261,18 +9261,41 @@ function unsupportedSetOptimisticState() { throw new Error("Cannot update optimistic state while rendering."); } -function unsupportedDispatchFormState() { - throw new Error("Cannot update form state while rendering."); -} - function useOptimistic(passthrough, reducer) { resolveCurrentlyRenderingComponent(); return [passthrough, unsupportedSetOptimisticState]; } -function useFormState(action, initialState, url) { - resolveCurrentlyRenderingComponent(); - return [initialState, unsupportedDispatchFormState]; +function useFormState(action, initialState, permalink) { + resolveCurrentlyRenderingComponent(); // Bind the initial state to the first argument of the action. + // TODO: Use the keypath (or permalink) to check if there's matching state + // from the previous page. + + var boundAction = action.bind(null, initialState); // Wrap the action so the return value is void. + + var dispatch = function (payload) { + boundAction(payload); + }; // $FlowIgnore[prop-missing] + + if (typeof boundAction.$$FORM_ACTION === "function") { + // $FlowIgnore[prop-missing] + dispatch.$$FORM_ACTION = function (prefix) { + // $FlowIgnore[prop-missing] + var metadata = boundAction.$$FORM_ACTION(prefix); // Override the target URL + + if (permalink !== undefined) { + { + checkAttributeStringCoercion(permalink, "target"); + } + + metadata.target = permalink + ""; + } + + return metadata; + }; + } + + return [initialState, dispatch]; } function useId() { diff --git a/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js b/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js index dfa00d5414..3ec2175600 100644 --- a/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js +++ b/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js @@ -2621,16 +2621,23 @@ function unsupportedStartTransition() { function unsupportedSetOptimisticState() { throw Error("Cannot update optimistic state while rendering."); } -function unsupportedDispatchFormState() { - throw Error("Cannot update form state while rendering."); -} function useOptimistic(passthrough) { resolveCurrentlyRenderingComponent(); return [passthrough, unsupportedSetOptimisticState]; } -function useFormState(action, initialState) { +function useFormState(action, initialState, permalink) { + function dispatch(payload) { + boundAction(payload); + } resolveCurrentlyRenderingComponent(); - return [initialState, unsupportedDispatchFormState]; + var boundAction = action.bind(null, initialState); + "function" === typeof boundAction.$$FORM_ACTION && + (dispatch.$$FORM_ACTION = function (prefix) { + prefix = boundAction.$$FORM_ACTION(prefix); + void 0 !== permalink && (prefix.target = permalink + ""); + return prefix; + }); + return [initialState, dispatch]; } function unwrapThenable(thenable) { var index = thenableIndexCounter; diff --git a/compiled/facebook-www/ReactDOMTesting-dev.classic.js b/compiled/facebook-www/ReactDOMTesting-dev.classic.js index c8a6e2acbd..22cfe4ad4a 100644 --- a/compiled/facebook-www/ReactDOMTesting-dev.classic.js +++ b/compiled/facebook-www/ReactDOMTesting-dev.classic.js @@ -972,7 +972,7 @@ function useFormStatus() { throw new Error("Not implemented."); } } -function useFormState(action, initialState, url) { +function useFormState(action, initialState, permalink) { { throw new Error("Not implemented."); } @@ -34625,7 +34625,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-1da36440"; +var ReactVersion = "18.3.0-www-classic-8046a020"; function createPortal$1( children, diff --git a/compiled/facebook-www/ReactDOMTesting-dev.modern.js b/compiled/facebook-www/ReactDOMTesting-dev.modern.js index 14fb9f1286..a28c9f25ad 100644 --- a/compiled/facebook-www/ReactDOMTesting-dev.modern.js +++ b/compiled/facebook-www/ReactDOMTesting-dev.modern.js @@ -144,7 +144,7 @@ function useFormStatus() { throw new Error("Not implemented."); } } -function useFormState(action, initialState, url) { +function useFormState(action, initialState, permalink) { { throw new Error("Not implemented."); } @@ -34470,7 +34470,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-modern-5c7325c9"; +var ReactVersion = "18.3.0-www-modern-c494fa06"; function createPortal$1( children, diff --git a/compiled/facebook-www/ReactTestRenderer-dev.classic.js b/compiled/facebook-www/ReactTestRenderer-dev.classic.js index 6269f0953b..2a2246c29f 100644 --- a/compiled/facebook-www/ReactTestRenderer-dev.classic.js +++ b/compiled/facebook-www/ReactTestRenderer-dev.classic.js @@ -24356,7 +24356,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-1da36440"; +var ReactVersion = "18.3.0-www-classic-8046a020"; // Might add PROFILE later.