From 4f8627064980180bcc0ae832453768e99c3287dd Mon Sep 17 00:00:00 2001 From: Josh Story Date: Wed, 9 Oct 2024 15:28:33 -0700 Subject: [PATCH] [Flight] support rendering Date's as top level values renderModelDesctructive can sometimes be called direclty on Date values. When this happens we don't first call toJSON on the Date value so we need to explicitly handle the case where where the rendered value is a Date instance as well. This change updates renderModelDesctructive to account for sometimes receiving Date instances directly. --- .../src/__tests__/ReactFlight-test.js | 12 ++++++++++++ packages/react-server/src/ReactFlightServer.js | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 857ce99868d9b..35fe8fef0354d 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -661,6 +661,18 @@ describe('ReactFlight', () => { `); }); + it('can transport Date as a top-level value', async () => { + const date = new Date(0); + const transport = ReactNoopFlightServer.render(date); + + let readValue; + await act(async () => { + readValue = await ReactNoopFlightClient.read(transport); + }); + + expect(readValue).toEqual(date); + }); + it('can transport Error objects as values', async () => { function ComponentClient({prop}) { return ` diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 5db03d628146f..0b1a4d3c7fee1 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -1962,6 +1962,12 @@ function serializeUndefined(): string { return '$undefined'; } +function serializeDate(date: Date): string { + // JSON.stringify automatically calls Date.prototype.toJSON which calls toISOString. + // We need only tack on a $D prefix. + return '$D' + date.toJSON(); +} + function serializeDateFromDateJSON(dateJSON: string): string { // JSON.stringify automatically calls Date.prototype.toJSON which calls toISOString. // We need only tack on a $D prefix. @@ -2779,6 +2785,14 @@ function renderModelDestructive( } } + // We put the Date check low b/c most of the time Date's will already have been serialized + // before we process it in this function but when rendering a Date() as a top level it can + // end up being a Date instance here. This is rare so we deprioritize it by putting it deep + // in this function + if (value instanceof Date) { + return serializeDate(value); + } + // Verify that this is a simple plain object. const proto = getPrototypeOf(value); if ( @@ -3646,6 +3660,10 @@ function renderConsoleValue( return serializeBigInt(value); } + if (value instanceof Date) { + return serializeDate(value); + } + return 'unknown type ' + typeof value; }