Skip to content

Commit c499adf

Browse files
authored
[Flight] Allow Temporary References to be awaited (facebook#34084)
Fixes facebook#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`.
1 parent 1d16396 commit c499adf

File tree

3 files changed

+49
-37
lines changed

3 files changed

+49
-37
lines changed

packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,50 @@ describe('ReactFlightDOMReply', () => {
438438
expect(response.obj).toBe(obj);
439439
});
440440

441+
it('can return an opaque object through an async function', async () => {
442+
function fn() {
443+
return 'this is a client function';
444+
}
445+
446+
const args = [fn];
447+
448+
const temporaryReferences =
449+
ReactServerDOMClient.createTemporaryReferenceSet();
450+
const body = await ReactServerDOMClient.encodeReply(args, {
451+
temporaryReferences,
452+
});
453+
454+
const temporaryReferencesServer =
455+
ReactServerDOMServer.createTemporaryReferenceSet();
456+
const serverPayload = await ReactServerDOMServer.decodeReply(
457+
body,
458+
webpackServerMap,
459+
{temporaryReferences: temporaryReferencesServer},
460+
);
461+
462+
async function action(arg) {
463+
return arg;
464+
}
465+
466+
const stream = await serverAct(() =>
467+
ReactServerDOMServer.renderToReadableStream(
468+
{
469+
result: action.apply(null, serverPayload),
470+
},
471+
null,
472+
{temporaryReferences: temporaryReferencesServer},
473+
),
474+
);
475+
const response = await ReactServerDOMClient.createFromReadableStream(
476+
stream,
477+
{
478+
temporaryReferences,
479+
},
480+
);
481+
482+
expect(await response.result).toBe(fn);
483+
});
484+
441485
it('should supports streaming ReadableStream with objects', async () => {
442486
let controller1;
443487
let controller2;

packages/react-server/src/ReactFlightServerTemporaryReferences.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,12 @@ const proxyHandlers = {
7070
`Instead, you can export a Client Component wrapper ` +
7171
`that itself renders a Client Context Provider.`,
7272
);
73-
// Allow returning a temporary reference from an async function
7473
case 'then':
74+
// Allow returning a temporary reference from an async function
75+
// Unlike regular Client References, a Promise would never have been serialized as
76+
// an opaque Temporary Reference, but instead would have been serialized as a
77+
// Promise on the server and so doesn't hit this path. So we can assume this wasn't
78+
// a Promise on the client.
7579
return undefined;
7680
}
7781
throw new Error(

packages/react-server/src/__tests__/ReactFlightServerTemporaryReferences-test.js

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)