From f9af3854d3a10bd7e8093de8004a655f0a90482e Mon Sep 17 00:00:00 2001 From: eps1lon Date: Wed, 8 Mar 2023 21:36:16 +0100 Subject: [PATCH 1/4] Add test for rendering undefined --- .../src/__tests__/ReactFlight-test.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 0e507876d96..c678ad94c7b 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -197,6 +197,22 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(ABC); }); + it('can render undefined', async () => { + function Undefined() { + return undefined; + } + + const model = ; + + const transport = ReactNoopFlightServer.render(model); + + await act(async () => { + ReactNoop.render(await ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(null); + }); + it('can render a lazy component as a shared component on the server', async () => { function SharedComponent({text}) { return ( From b26341931dbc2e7912eb38b4e78f16b19b3625dc Mon Sep 17 00:00:00 2001 From: eps1lon Date: Wed, 8 Mar 2023 21:36:42 +0100 Subject: [PATCH 2/4] [Flight] Support returning undefined --- packages/react-client/src/ReactFlightClient.js | 4 ++++ packages/react-server/src/ReactFlightServer.js | 15 ++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index fe5532de5c3..8a073a8dac1 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -556,6 +556,10 @@ export function parseModelString( throw chunk.reason; } } + case 'U': { + // Special encoding for `undefined` which can't be serialized as JSON otherwise. + return undefined; + } default: { // We assume that anything else is a reference ID. const id = parseInt(value.substring(1), 16); diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 2f82e024b67..80a860be41e 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -117,6 +117,7 @@ export type ReactClientValue = | number | symbol | null + | void | Iterable | Array | ReactClientObject @@ -546,6 +547,10 @@ function serializeProviderReference(name: string): string { return '$P' + name; } +function serializeUndefined(): string { + return '$U'; +} + function serializeClientReference( request: Request, parent: @@ -1134,14 +1139,14 @@ export function resolveModelToJSON( return escapeStringValue(value); } - if ( - typeof value === 'boolean' || - typeof value === 'number' || - typeof value === 'undefined' - ) { + if (typeof value === 'boolean' || typeof value === 'number') { return value; } + if (typeof value === 'undefined') { + return serializeUndefined(); + } + if (typeof value === 'function') { if (isClientReference(value)) { return serializeClientReference(request, parent, key, (value: any)); From 489ecc4de5607e209ee6a0781cf672b4cbf7f1eb Mon Sep 17 00:00:00 2001 From: eps1lon Date: Wed, 8 Mar 2023 21:44:36 +0100 Subject: [PATCH 3/4] Regression test for empty fragments This was also throwing before we added support for rendering undefined. --- .../src/__tests__/ReactFlight-test.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index c678ad94c7b..be92bb2d9eb 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -213,6 +213,22 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(null); }); + it('can render an empty fragment', async () => { + function Empty() { + return ; + } + + const model = ; + + const transport = ReactNoopFlightServer.render(model); + + await act(async () => { + ReactNoop.render(await ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(null); + }); + it('can render a lazy component as a shared component on the server', async () => { function SharedComponent({text}) { return ( From 97322599b43bfe29177281d29fe91936127e4769 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Thu, 9 Mar 2023 09:58:27 +0100 Subject: [PATCH 4/4] $U -> $undefined --- packages/react-client/src/ReactFlightClient.js | 3 ++- packages/react-server/src/ReactFlightServer.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 8a073a8dac1..ea6a09619ed 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -556,7 +556,8 @@ export function parseModelString( throw chunk.reason; } } - case 'U': { + case 'u': { + // matches "$undefined" // Special encoding for `undefined` which can't be serialized as JSON otherwise. return undefined; } diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 80a860be41e..5f7319972d8 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -548,7 +548,7 @@ function serializeProviderReference(name: string): string { } function serializeUndefined(): string { - return '$U'; + return '$undefined'; } function serializeClientReference(