Skip to content

Commit

Permalink
[Fizz] Allow an action provide a custom set of props to use for progr…
Browse files Browse the repository at this point in the history
…essive 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)
  • Loading branch information
sebmarkbage committed May 1, 2023
1 parent 6476ee0 commit 0b3882c
Show file tree
Hide file tree
Showing 7 changed files with 401 additions and 284 deletions.
2 changes: 1 addition & 1 deletion compiled/facebook-www/REVISION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
67f4fb02130b1fe1856289e3b66bb0b8cca57ff7
559e83aebb2026035d47aa0ebf842f78d4cd6757
101 changes: 64 additions & 37 deletions compiled/facebook-www/ReactDOMServer-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ if (__DEV__) {
var React = require("react");
var ReactDOM = require("react-dom");

var ReactVersion = "18.3.0-www-classic-fcee71d2";
var ReactVersion = "18.3.0-www-classic-ccb1f72b";

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

stringToPrecomputedChunk(
Expand All @@ -2849,6 +2849,30 @@ stringToPrecomputedChunk(
"javascript:throw new Error('A React form was unexpectedly submitted.')"
)
);
var startHiddenInputChunk = stringToPrecomputedChunk('<input type="hidden"');

function pushAdditionalFormField(value, key) {
var target = this;
target.push(startHiddenInputChunk);

if (typeof value !== "string") {
throw new Error(
"File/Blob fields are not yet supported in progressive forms. " +
"It probably means you are closing over binary data or FormData in a Server Action."
);
}

pushStringAttribute(target, "name", key);
pushStringAttribute(target, "value", value);
target.push(endOfStartTagSelfClosing);
}

function pushAdditionalFormFields(target, formData) {
if (formData !== null) {
// $FlowFixMe[prop-missing]: FormData has forEach.
formData.forEach(pushAdditionalFormField, target);
}
}

function pushFormActionAttribute(
target,
Expand All @@ -2859,28 +2883,29 @@ function pushFormActionAttribute(
formTarget,
name
) {
{
// Plain form actions support all the properties, so we have to emit them.
if (name !== null) {
pushAttribute(target, "name", name);
}
var formData = null;

if (formAction !== null) {
pushAttribute(target, "formAction", formAction);
}
if (name !== null) {
pushAttribute(target, "name", name);
}

if (formEncType !== null) {
pushAttribute(target, "formEncType", formEncType);
}
if (formAction !== null) {
pushAttribute(target, "formAction", formAction);
}

if (formMethod !== null) {
pushAttribute(target, "formMethod", formMethod);
}
if (formEncType !== null) {
pushAttribute(target, "formEncType", formEncType);
}

if (formTarget !== null) {
pushAttribute(target, "formTarget", formTarget);
}
if (formMethod !== null) {
pushAttribute(target, "formMethod", formMethod);
}

if (formTarget !== null) {
pushAttribute(target, "formTarget", formTarget);
}

return formData;
}

function pushAttribute(target, name, value) {
Expand Down Expand Up @@ -3535,26 +3560,24 @@ function pushStartForm(target, props, responseState) {
}
}

{
// Plain form actions support all the properties, so we have to emit them.
if (formAction !== null) {
pushAttribute(target, "action", formAction);
}
if (formAction !== null) {
pushAttribute(target, "action", formAction);
}

if (formEncType !== null) {
pushAttribute(target, "encType", formEncType);
}
if (formEncType !== null) {
pushAttribute(target, "encType", formEncType);
}

if (formMethod !== null) {
pushAttribute(target, "method", formMethod);
}
if (formMethod !== null) {
pushAttribute(target, "method", formMethod);
}

if (formTarget !== null) {
pushAttribute(target, "target", formTarget);
}
if (formTarget !== null) {
pushAttribute(target, "target", formTarget);
}

target.push(endOfStartTag);

pushInnerHTML(target, innerHTML, children);

if (typeof children === "string") {
Expand Down Expand Up @@ -3658,7 +3681,7 @@ function pushInput(target, props, responseState) {
}
}

pushFormActionAttribute(
var formData = pushFormActionAttribute(
target,
responseState,
formAction,
Expand Down Expand Up @@ -3712,7 +3735,9 @@ function pushInput(target, props, responseState) {
pushAttribute(target, "value", defaultValue);
}

target.push(endOfStartTagSelfClosing);
target.push(endOfStartTagSelfClosing); // We place any additional hidden form fields after the input.

pushAdditionalFormFields(target, formData);
return null;
}

Expand Down Expand Up @@ -3785,7 +3810,7 @@ function pushStartButton(target, props, responseState) {
}
}

pushFormActionAttribute(
var formData = pushFormActionAttribute(
target,
responseState,
formAction,
Expand All @@ -3794,7 +3819,9 @@ function pushStartButton(target, props, responseState) {
formTarget,
name
);
target.push(endOfStartTag);
target.push(endOfStartTag); // We place any additional hidden form fields we need to include inside the button itself.

pushAdditionalFormFields(target, formData);
pushInnerHTML(target, innerHTML, children);

if (typeof children === "string") {
Expand Down
101 changes: 64 additions & 37 deletions compiled/facebook-www/ReactDOMServer-dev.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ if (__DEV__) {
var React = require("react");
var ReactDOM = require("react-dom");

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

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

stringToPrecomputedChunk(
Expand All @@ -2849,6 +2849,30 @@ stringToPrecomputedChunk(
"javascript:throw new Error('A React form was unexpectedly submitted.')"
)
);
var startHiddenInputChunk = stringToPrecomputedChunk('<input type="hidden"');

function pushAdditionalFormField(value, key) {
var target = this;
target.push(startHiddenInputChunk);

if (typeof value !== "string") {
throw new Error(
"File/Blob fields are not yet supported in progressive forms. " +
"It probably means you are closing over binary data or FormData in a Server Action."
);
}

pushStringAttribute(target, "name", key);
pushStringAttribute(target, "value", value);
target.push(endOfStartTagSelfClosing);
}

function pushAdditionalFormFields(target, formData) {
if (formData !== null) {
// $FlowFixMe[prop-missing]: FormData has forEach.
formData.forEach(pushAdditionalFormField, target);
}
}

function pushFormActionAttribute(
target,
Expand All @@ -2859,28 +2883,29 @@ function pushFormActionAttribute(
formTarget,
name
) {
{
// Plain form actions support all the properties, so we have to emit them.
if (name !== null) {
pushAttribute(target, "name", name);
}
var formData = null;

if (formAction !== null) {
pushAttribute(target, "formAction", formAction);
}
if (name !== null) {
pushAttribute(target, "name", name);
}

if (formEncType !== null) {
pushAttribute(target, "formEncType", formEncType);
}
if (formAction !== null) {
pushAttribute(target, "formAction", formAction);
}

if (formMethod !== null) {
pushAttribute(target, "formMethod", formMethod);
}
if (formEncType !== null) {
pushAttribute(target, "formEncType", formEncType);
}

if (formTarget !== null) {
pushAttribute(target, "formTarget", formTarget);
}
if (formMethod !== null) {
pushAttribute(target, "formMethod", formMethod);
}

if (formTarget !== null) {
pushAttribute(target, "formTarget", formTarget);
}

return formData;
}

function pushAttribute(target, name, value) {
Expand Down Expand Up @@ -3535,26 +3560,24 @@ function pushStartForm(target, props, responseState) {
}
}

{
// Plain form actions support all the properties, so we have to emit them.
if (formAction !== null) {
pushAttribute(target, "action", formAction);
}
if (formAction !== null) {
pushAttribute(target, "action", formAction);
}

if (formEncType !== null) {
pushAttribute(target, "encType", formEncType);
}
if (formEncType !== null) {
pushAttribute(target, "encType", formEncType);
}

if (formMethod !== null) {
pushAttribute(target, "method", formMethod);
}
if (formMethod !== null) {
pushAttribute(target, "method", formMethod);
}

if (formTarget !== null) {
pushAttribute(target, "target", formTarget);
}
if (formTarget !== null) {
pushAttribute(target, "target", formTarget);
}

target.push(endOfStartTag);

pushInnerHTML(target, innerHTML, children);

if (typeof children === "string") {
Expand Down Expand Up @@ -3658,7 +3681,7 @@ function pushInput(target, props, responseState) {
}
}

pushFormActionAttribute(
var formData = pushFormActionAttribute(
target,
responseState,
formAction,
Expand Down Expand Up @@ -3712,7 +3735,9 @@ function pushInput(target, props, responseState) {
pushAttribute(target, "value", defaultValue);
}

target.push(endOfStartTagSelfClosing);
target.push(endOfStartTagSelfClosing); // We place any additional hidden form fields after the input.

pushAdditionalFormFields(target, formData);
return null;
}

Expand Down Expand Up @@ -3785,7 +3810,7 @@ function pushStartButton(target, props, responseState) {
}
}

pushFormActionAttribute(
var formData = pushFormActionAttribute(
target,
responseState,
formAction,
Expand All @@ -3794,7 +3819,9 @@ function pushStartButton(target, props, responseState) {
formTarget,
name
);
target.push(endOfStartTag);
target.push(endOfStartTag); // We place any additional hidden form fields we need to include inside the button itself.

pushAdditionalFormFields(target, formData);
pushInnerHTML(target, innerHTML, children);

if (typeof children === "string") {
Expand Down
Loading

0 comments on commit 0b3882c

Please sign in to comment.