From 9c79367ba7631191b2c465f2d338560a10bfaa3c Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Sat, 2 Aug 2025 18:05:16 -0400 Subject: [PATCH] Allow Temporary References to be awaited --- .../src/__tests__/ReactFlightDOMReply-test.js | 44 +++++++++++++++++++ .../ReactFlightServerTemporaryReferences.js | 6 ++- ...actFlightServerTemporaryReferences-test.js | 36 --------------- 3 files changed, 49 insertions(+), 37 deletions(-) delete mode 100644 packages/react-server/src/__tests__/ReactFlightServerTemporaryReferences-test.js diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js index 6e113556206f4..2181eb5fe700c 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js @@ -438,6 +438,50 @@ describe('ReactFlightDOMReply', () => { expect(response.obj).toBe(obj); }); + it('can return an opaque object through an async function', async () => { + function fn() { + return 'this is a client function'; + } + + const args = [fn]; + + const temporaryReferences = + ReactServerDOMClient.createTemporaryReferenceSet(); + const body = await ReactServerDOMClient.encodeReply(args, { + temporaryReferences, + }); + + const temporaryReferencesServer = + ReactServerDOMServer.createTemporaryReferenceSet(); + const serverPayload = await ReactServerDOMServer.decodeReply( + body, + webpackServerMap, + {temporaryReferences: temporaryReferencesServer}, + ); + + async function action(arg) { + return arg; + } + + const stream = await serverAct(() => + ReactServerDOMServer.renderToReadableStream( + { + result: action.apply(null, serverPayload), + }, + null, + {temporaryReferences: temporaryReferencesServer}, + ), + ); + const response = await ReactServerDOMClient.createFromReadableStream( + stream, + { + temporaryReferences, + }, + ); + + expect(await response.result).toBe(fn); + }); + it('should supports streaming ReadableStream with objects', async () => { let controller1; let controller2; diff --git a/packages/react-server/src/ReactFlightServerTemporaryReferences.js b/packages/react-server/src/ReactFlightServerTemporaryReferences.js index e28b603edf9b5..1ea89c81980c1 100644 --- a/packages/react-server/src/ReactFlightServerTemporaryReferences.js +++ b/packages/react-server/src/ReactFlightServerTemporaryReferences.js @@ -70,8 +70,12 @@ const proxyHandlers = { `Instead, you can export a Client Component wrapper ` + `that itself renders a Client Context Provider.`, ); - // Allow returning a temporary reference from an async function case 'then': + // Allow returning a temporary reference from an async function + // Unlike regular Client References, a Promise would never have been serialized as + // an opaque Temporary Reference, but instead would have been serialized as a + // Promise on the server and so doesn't hit this path. So we can assume this wasn't + // a Promise on the client. return undefined; } throw new Error( diff --git a/packages/react-server/src/__tests__/ReactFlightServerTemporaryReferences-test.js b/packages/react-server/src/__tests__/ReactFlightServerTemporaryReferences-test.js deleted file mode 100644 index 7e93179268d50..0000000000000 --- a/packages/react-server/src/__tests__/ReactFlightServerTemporaryReferences-test.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @emails react-core - * @jest-environment node - */ - -'use strict'; - -let ReactFlightServerTemporaryReferences; - -describe('ReactFlightServerTemporaryReferences', () => { - beforeEach(() => { - jest.resetModules(); - ReactFlightServerTemporaryReferences = require('react-server/src/ReactFlightServerTemporaryReferences'); - }); - - it('can return a temporary reference from an async function', async () => { - const temporaryReferenceSet = - ReactFlightServerTemporaryReferences.createTemporaryReferenceSet(); - const temporaryReference = - ReactFlightServerTemporaryReferences.createTemporaryReference( - temporaryReferenceSet, - 'test', - ); - - async function foo() { - return temporaryReference; - } - - await expect(foo()).resolves.toBe(temporaryReference); - }); -});