Skip to content

Commit eee6951

Browse files
committed
Support FormData from Server to Client
1 parent fd0da3e commit eee6951

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,16 @@ function parseModelString(
736736
const data = getOutlinedModel(response, id);
737737
return new Set(data);
738738
}
739+
case 'K': {
740+
// FormData
741+
const id = parseInt(value.slice(2), 16);
742+
const data = getOutlinedModel(response, id);
743+
const formData = new FormData();
744+
for (let i = 0; i < data.length; i++) {
745+
formData.append(data[i][0], data[i][1]);
746+
}
747+
return formData;
748+
}
739749
case 'I': {
740750
// $Infinity
741751
return Infinity;

packages/react-client/src/__tests__/ReactFlight-test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,40 @@ describe('ReactFlight', () => {
468468
`);
469469
});
470470

471+
if (typeof FormData !== 'undefined') {
472+
it('can transport FormData (no blobs)', async () => {
473+
function ComponentClient({prop}) {
474+
return `
475+
formData: ${prop instanceof FormData}
476+
hi: ${prop.get('hi')}
477+
multiple: ${prop.getAll('multiple')}
478+
content: ${JSON.stringify(Array.from(prop))}
479+
`;
480+
}
481+
const Component = clientReference(ComponentClient);
482+
483+
const formData = new FormData();
484+
formData.append('hi', 'world');
485+
formData.append('multiple', 1);
486+
formData.append('multiple', 2);
487+
488+
const model = <Component prop={formData} />;
489+
490+
const transport = ReactNoopFlightServer.render(model);
491+
492+
await act(async () => {
493+
ReactNoop.render(await ReactNoopFlightClient.read(transport));
494+
});
495+
496+
expect(ReactNoop).toMatchRenderedOutput(`
497+
formData: true
498+
hi: world
499+
multiple: 1,2
500+
content: [["hi","world"],["multiple","1"],["multiple","2"]]
501+
`);
502+
});
503+
}
504+
471505
it('can transport cyclic objects', async () => {
472506
function ComponentClient({prop}) {
473507
expect(prop.obj.obj.obj).toBe(prop.obj.obj);

packages/react-server/src/ReactFlightServer.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,12 @@ function serializeMap(
11381138
return '$Q' + id.toString(16);
11391139
}
11401140

1141+
function serializeFormData(request: Request, formData: FormData): string {
1142+
const entries = Array.from(formData.entries());
1143+
const id = outlineModel(request, (entries: any));
1144+
return '$K' + id.toString(16);
1145+
}
1146+
11411147
function serializeSet(request: Request, set: Set<ReactClientValue>): string {
11421148
const entries = Array.from(set);
11431149
for (let i = 0; i < entries.length; i++) {
@@ -1506,6 +1512,10 @@ function renderModelDestructive(
15061512
if (value instanceof Set) {
15071513
return serializeSet(request, value);
15081514
}
1515+
// TODO: FormData is not available in old Node. Remove the typeof later.
1516+
if (typeof FormData === 'function' && value instanceof FormData) {
1517+
return serializeFormData(request, value);
1518+
}
15091519

15101520
if (enableBinaryFlight) {
15111521
if (value instanceof ArrayBuffer) {
@@ -2027,6 +2037,10 @@ function renderConsoleValue(
20272037
if (value instanceof Set) {
20282038
return serializeSet(request, value);
20292039
}
2040+
// TODO: FormData is not available in old Node. Remove the typeof later.
2041+
if (typeof FormData === 'function' && value instanceof FormData) {
2042+
return serializeFormData(request, value);
2043+
}
20302044

20312045
if (enableBinaryFlight) {
20322046
if (value instanceof ArrayBuffer) {

0 commit comments

Comments
 (0)