Skip to content

Commit 0b3882c

Browse files
committed
[Fizz] Allow an action provide a custom set of props to use for progressive enhancement (#26749)
Stacked on top of #26735. This allows a framework to add a `$$FORM_ACTION` property to a function. This lets the framework return a set of props to use in place of the function but only during SSR. Effectively, this lets you implement progressive enhancement of form actions using some other way instead of relying on the replay feature. This will be used by RSC on Server References automatically by convention in a follow up, but this mechanism can also be used by other frameworks/libraries. DiffTrain build for [559e83a](559e83a)
1 parent 6476ee0 commit 0b3882c

7 files changed

+401
-284
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
67f4fb02130b1fe1856289e3b66bb0b8cca57ff7
1+
559e83aebb2026035d47aa0ebf842f78d4cd6757

compiled/facebook-www/ReactDOMServer-dev.classic.js

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if (__DEV__) {
1919
var React = require("react");
2020
var ReactDOM = require("react-dom");
2121

22-
var ReactVersion = "18.3.0-www-classic-fcee71d2";
22+
var ReactVersion = "18.3.0-www-classic-ccb1f72b";
2323

2424
// This refers to a WWW module.
2525
var warningWWW = require("warning");
@@ -2840,7 +2840,7 @@ function pushStringAttribute(target, name, value) {
28402840
attributeEnd
28412841
);
28422842
}
2843-
} // Since this will likely be repeated a lot in the HTML, we use a more concise message
2843+
}
28442844
// than on the client and hopefully it's googleable.
28452845

28462846
stringToPrecomputedChunk(
@@ -2849,6 +2849,30 @@ stringToPrecomputedChunk(
28492849
"javascript:throw new Error('A React form was unexpectedly submitted.')"
28502850
)
28512851
);
2852+
var startHiddenInputChunk = stringToPrecomputedChunk('<input type="hidden"');
2853+
2854+
function pushAdditionalFormField(value, key) {
2855+
var target = this;
2856+
target.push(startHiddenInputChunk);
2857+
2858+
if (typeof value !== "string") {
2859+
throw new Error(
2860+
"File/Blob fields are not yet supported in progressive forms. " +
2861+
"It probably means you are closing over binary data or FormData in a Server Action."
2862+
);
2863+
}
2864+
2865+
pushStringAttribute(target, "name", key);
2866+
pushStringAttribute(target, "value", value);
2867+
target.push(endOfStartTagSelfClosing);
2868+
}
2869+
2870+
function pushAdditionalFormFields(target, formData) {
2871+
if (formData !== null) {
2872+
// $FlowFixMe[prop-missing]: FormData has forEach.
2873+
formData.forEach(pushAdditionalFormField, target);
2874+
}
2875+
}
28522876

28532877
function pushFormActionAttribute(
28542878
target,
@@ -2859,28 +2883,29 @@ function pushFormActionAttribute(
28592883
formTarget,
28602884
name
28612885
) {
2862-
{
2863-
// Plain form actions support all the properties, so we have to emit them.
2864-
if (name !== null) {
2865-
pushAttribute(target, "name", name);
2866-
}
2886+
var formData = null;
28672887

2868-
if (formAction !== null) {
2869-
pushAttribute(target, "formAction", formAction);
2870-
}
2888+
if (name !== null) {
2889+
pushAttribute(target, "name", name);
2890+
}
28712891

2872-
if (formEncType !== null) {
2873-
pushAttribute(target, "formEncType", formEncType);
2874-
}
2892+
if (formAction !== null) {
2893+
pushAttribute(target, "formAction", formAction);
2894+
}
28752895

2876-
if (formMethod !== null) {
2877-
pushAttribute(target, "formMethod", formMethod);
2878-
}
2896+
if (formEncType !== null) {
2897+
pushAttribute(target, "formEncType", formEncType);
2898+
}
28792899

2880-
if (formTarget !== null) {
2881-
pushAttribute(target, "formTarget", formTarget);
2882-
}
2900+
if (formMethod !== null) {
2901+
pushAttribute(target, "formMethod", formMethod);
28832902
}
2903+
2904+
if (formTarget !== null) {
2905+
pushAttribute(target, "formTarget", formTarget);
2906+
}
2907+
2908+
return formData;
28842909
}
28852910

28862911
function pushAttribute(target, name, value) {
@@ -3535,26 +3560,24 @@ function pushStartForm(target, props, responseState) {
35353560
}
35363561
}
35373562

3538-
{
3539-
// Plain form actions support all the properties, so we have to emit them.
3540-
if (formAction !== null) {
3541-
pushAttribute(target, "action", formAction);
3542-
}
3563+
if (formAction !== null) {
3564+
pushAttribute(target, "action", formAction);
3565+
}
35433566

3544-
if (formEncType !== null) {
3545-
pushAttribute(target, "encType", formEncType);
3546-
}
3567+
if (formEncType !== null) {
3568+
pushAttribute(target, "encType", formEncType);
3569+
}
35473570

3548-
if (formMethod !== null) {
3549-
pushAttribute(target, "method", formMethod);
3550-
}
3571+
if (formMethod !== null) {
3572+
pushAttribute(target, "method", formMethod);
3573+
}
35513574

3552-
if (formTarget !== null) {
3553-
pushAttribute(target, "target", formTarget);
3554-
}
3575+
if (formTarget !== null) {
3576+
pushAttribute(target, "target", formTarget);
35553577
}
35563578

35573579
target.push(endOfStartTag);
3580+
35583581
pushInnerHTML(target, innerHTML, children);
35593582

35603583
if (typeof children === "string") {
@@ -3658,7 +3681,7 @@ function pushInput(target, props, responseState) {
36583681
}
36593682
}
36603683

3661-
pushFormActionAttribute(
3684+
var formData = pushFormActionAttribute(
36623685
target,
36633686
responseState,
36643687
formAction,
@@ -3712,7 +3735,9 @@ function pushInput(target, props, responseState) {
37123735
pushAttribute(target, "value", defaultValue);
37133736
}
37143737

3715-
target.push(endOfStartTagSelfClosing);
3738+
target.push(endOfStartTagSelfClosing); // We place any additional hidden form fields after the input.
3739+
3740+
pushAdditionalFormFields(target, formData);
37163741
return null;
37173742
}
37183743

@@ -3785,7 +3810,7 @@ function pushStartButton(target, props, responseState) {
37853810
}
37863811
}
37873812

3788-
pushFormActionAttribute(
3813+
var formData = pushFormActionAttribute(
37893814
target,
37903815
responseState,
37913816
formAction,
@@ -3794,7 +3819,9 @@ function pushStartButton(target, props, responseState) {
37943819
formTarget,
37953820
name
37963821
);
3797-
target.push(endOfStartTag);
3822+
target.push(endOfStartTag); // We place any additional hidden form fields we need to include inside the button itself.
3823+
3824+
pushAdditionalFormFields(target, formData);
37983825
pushInnerHTML(target, innerHTML, children);
37993826

38003827
if (typeof children === "string") {

compiled/facebook-www/ReactDOMServer-dev.modern.js

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if (__DEV__) {
1919
var React = require("react");
2020
var ReactDOM = require("react-dom");
2121

22-
var ReactVersion = "18.3.0-www-modern-938c86f0";
22+
var ReactVersion = "18.3.0-www-modern-88959423";
2323

2424
// This refers to a WWW module.
2525
var warningWWW = require("warning");
@@ -2840,7 +2840,7 @@ function pushStringAttribute(target, name, value) {
28402840
attributeEnd
28412841
);
28422842
}
2843-
} // Since this will likely be repeated a lot in the HTML, we use a more concise message
2843+
}
28442844
// than on the client and hopefully it's googleable.
28452845

28462846
stringToPrecomputedChunk(
@@ -2849,6 +2849,30 @@ stringToPrecomputedChunk(
28492849
"javascript:throw new Error('A React form was unexpectedly submitted.')"
28502850
)
28512851
);
2852+
var startHiddenInputChunk = stringToPrecomputedChunk('<input type="hidden"');
2853+
2854+
function pushAdditionalFormField(value, key) {
2855+
var target = this;
2856+
target.push(startHiddenInputChunk);
2857+
2858+
if (typeof value !== "string") {
2859+
throw new Error(
2860+
"File/Blob fields are not yet supported in progressive forms. " +
2861+
"It probably means you are closing over binary data or FormData in a Server Action."
2862+
);
2863+
}
2864+
2865+
pushStringAttribute(target, "name", key);
2866+
pushStringAttribute(target, "value", value);
2867+
target.push(endOfStartTagSelfClosing);
2868+
}
2869+
2870+
function pushAdditionalFormFields(target, formData) {
2871+
if (formData !== null) {
2872+
// $FlowFixMe[prop-missing]: FormData has forEach.
2873+
formData.forEach(pushAdditionalFormField, target);
2874+
}
2875+
}
28522876

28532877
function pushFormActionAttribute(
28542878
target,
@@ -2859,28 +2883,29 @@ function pushFormActionAttribute(
28592883
formTarget,
28602884
name
28612885
) {
2862-
{
2863-
// Plain form actions support all the properties, so we have to emit them.
2864-
if (name !== null) {
2865-
pushAttribute(target, "name", name);
2866-
}
2886+
var formData = null;
28672887

2868-
if (formAction !== null) {
2869-
pushAttribute(target, "formAction", formAction);
2870-
}
2888+
if (name !== null) {
2889+
pushAttribute(target, "name", name);
2890+
}
28712891

2872-
if (formEncType !== null) {
2873-
pushAttribute(target, "formEncType", formEncType);
2874-
}
2892+
if (formAction !== null) {
2893+
pushAttribute(target, "formAction", formAction);
2894+
}
28752895

2876-
if (formMethod !== null) {
2877-
pushAttribute(target, "formMethod", formMethod);
2878-
}
2896+
if (formEncType !== null) {
2897+
pushAttribute(target, "formEncType", formEncType);
2898+
}
28792899

2880-
if (formTarget !== null) {
2881-
pushAttribute(target, "formTarget", formTarget);
2882-
}
2900+
if (formMethod !== null) {
2901+
pushAttribute(target, "formMethod", formMethod);
28832902
}
2903+
2904+
if (formTarget !== null) {
2905+
pushAttribute(target, "formTarget", formTarget);
2906+
}
2907+
2908+
return formData;
28842909
}
28852910

28862911
function pushAttribute(target, name, value) {
@@ -3535,26 +3560,24 @@ function pushStartForm(target, props, responseState) {
35353560
}
35363561
}
35373562

3538-
{
3539-
// Plain form actions support all the properties, so we have to emit them.
3540-
if (formAction !== null) {
3541-
pushAttribute(target, "action", formAction);
3542-
}
3563+
if (formAction !== null) {
3564+
pushAttribute(target, "action", formAction);
3565+
}
35433566

3544-
if (formEncType !== null) {
3545-
pushAttribute(target, "encType", formEncType);
3546-
}
3567+
if (formEncType !== null) {
3568+
pushAttribute(target, "encType", formEncType);
3569+
}
35473570

3548-
if (formMethod !== null) {
3549-
pushAttribute(target, "method", formMethod);
3550-
}
3571+
if (formMethod !== null) {
3572+
pushAttribute(target, "method", formMethod);
3573+
}
35513574

3552-
if (formTarget !== null) {
3553-
pushAttribute(target, "target", formTarget);
3554-
}
3575+
if (formTarget !== null) {
3576+
pushAttribute(target, "target", formTarget);
35553577
}
35563578

35573579
target.push(endOfStartTag);
3580+
35583581
pushInnerHTML(target, innerHTML, children);
35593582

35603583
if (typeof children === "string") {
@@ -3658,7 +3681,7 @@ function pushInput(target, props, responseState) {
36583681
}
36593682
}
36603683

3661-
pushFormActionAttribute(
3684+
var formData = pushFormActionAttribute(
36623685
target,
36633686
responseState,
36643687
formAction,
@@ -3712,7 +3735,9 @@ function pushInput(target, props, responseState) {
37123735
pushAttribute(target, "value", defaultValue);
37133736
}
37143737

3715-
target.push(endOfStartTagSelfClosing);
3738+
target.push(endOfStartTagSelfClosing); // We place any additional hidden form fields after the input.
3739+
3740+
pushAdditionalFormFields(target, formData);
37163741
return null;
37173742
}
37183743

@@ -3785,7 +3810,7 @@ function pushStartButton(target, props, responseState) {
37853810
}
37863811
}
37873812

3788-
pushFormActionAttribute(
3813+
var formData = pushFormActionAttribute(
37893814
target,
37903815
responseState,
37913816
formAction,
@@ -3794,7 +3819,9 @@ function pushStartButton(target, props, responseState) {
37943819
formTarget,
37953820
name
37963821
);
3797-
target.push(endOfStartTag);
3822+
target.push(endOfStartTag); // We place any additional hidden form fields we need to include inside the button itself.
3823+
3824+
pushAdditionalFormFields(target, formData);
37983825
pushInnerHTML(target, innerHTML, children);
37993826

38003827
if (typeof children === "string") {

0 commit comments

Comments
 (0)