Repository: facebook/react
Pull request: [Flight] Allow Temporary References to be awaited

Fixes #33534.

`.then` method can be tested when you await a value that's not a Promise. For regular Client References we have a way to mark those as "async" and yield a reference to the unwrapped value in case it's a Promise on the Client.

However, the realization is that we never serialize Promises as opaque when passed from the client to the server. If a Promise is passed, then it would've been deserialized as a Promise (while still registered as a temporary reference) and not one of these Proxy objects.

Technically it could be a non-function value on the client which would be wrong but you're not supposed to dot into it in the first place.

So we can just assume it's `undefined`.

Changed files:
filename: packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js
patch: @@ -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;

filename: packages/react-server/src/ReactFlightServerTemporaryReferences.js
patch: @@ -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(

filename: packages/react-server/src/__tests__/ReactFlightServerTemporaryReferences-test.js
patch: @@ -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);
-  });
-});