@@ -375,31 +380,34 @@ describe('ReactFlight', () => {
const symbol = ReactNoopFlightServer.render(
, options);
const refs = ReactNoopFlightServer.render(
, options);
- function Client({transport}) {
- return ReactNoopFlightClient.read(transport);
+ function Client({promise}) {
+ return use(promise);
}
- act(() => {
- ReactNoop.render(
- <>
-
-
-
-
-
-
-
-
-
-
-
-
- >,
- );
+ await act(async () => {
+ startTransition(() => {
+ ReactNoop.render(
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ >,
+ );
+ });
});
});
- it('should trigger the inner most error boundary inside a client component', () => {
+ // @gate enableUseHook
+ it('should trigger the inner most error boundary inside a client component', async () => {
function ServerComponent() {
throw new Error('This was thrown in the server component.');
}
@@ -433,16 +441,18 @@ describe('ReactFlight', () => {
},
});
- function Client({transport}) {
- return ReactNoopFlightClient.read(transport);
+ function Client({promise}) {
+ return use(promise);
}
- act(() => {
- ReactNoop.render(
-
-
- ,
- );
+ await act(async () => {
+ startTransition(() => {
+ ReactNoop.render(
+
+
+ ,
+ );
+ });
});
});
@@ -451,9 +461,7 @@ describe('ReactFlight', () => {
const transport = ReactNoopFlightServer.render(
,
);
- act(() => {
- ReactNoop.render(ReactNoopFlightClient.read(transport));
- });
+ ReactNoopFlightClient.read(transport);
}).toErrorDev(
'Only plain objects can be passed to client components from server components. ',
{withoutStack: true},
@@ -463,9 +471,7 @@ describe('ReactFlight', () => {
it('should warn in DEV if a special object is passed to a host component', () => {
expect(() => {
const transport = ReactNoopFlightServer.render(
);
- act(() => {
- ReactNoop.render(ReactNoopFlightClient.read(transport));
- });
+ ReactNoopFlightClient.read(transport);
}).toErrorDev(
'Only plain objects can be passed to client components from server components. ' +
'Built-ins like Math are not supported.',
@@ -475,9 +481,7 @@ describe('ReactFlight', () => {
it('should NOT warn in DEV for key getters', () => {
const transport = ReactNoopFlightServer.render(
);
- act(() => {
- ReactNoop.render(ReactNoopFlightClient.read(transport));
- });
+ ReactNoopFlightClient.read(transport);
});
it('should warn in DEV if an object with symbols is passed to a host component', () => {
@@ -485,9 +489,7 @@ describe('ReactFlight', () => {
const transport = ReactNoopFlightServer.render(
,
);
- act(() => {
- ReactNoop.render(ReactNoopFlightClient.read(transport));
- });
+ ReactNoopFlightClient.read(transport);
}).toErrorDev(
'Only plain objects can be passed to client components from server components. ' +
'Objects with symbol properties like Symbol.iterator are not supported.',
@@ -503,9 +505,7 @@ describe('ReactFlight', () => {
const transport = ReactNoopFlightServer.render(
,
);
- act(() => {
- ReactNoop.render(ReactNoopFlightClient.read(transport));
- });
+ ReactNoopFlightClient.read(transport);
}).toErrorDev(
'Only plain objects can be passed to client components from server components. ',
{withoutStack: true},
@@ -518,7 +518,7 @@ describe('ReactFlight', () => {
return
{children}
;
}
- it('should support useId', () => {
+ it('should support useId', async () => {
function App() {
return (
<>
@@ -529,8 +529,8 @@ describe('ReactFlight', () => {
}
const transport = ReactNoopFlightServer.render(
);
- act(() => {
- ReactNoop.render(ReactNoopFlightClient.read(transport));
+ await act(async () => {
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
});
expect(ReactNoop).toMatchRenderedOutput(
<>
@@ -540,7 +540,7 @@ describe('ReactFlight', () => {
);
});
- it('accepts an identifier prefix that prefixes generated ids', () => {
+ it('accepts an identifier prefix that prefixes generated ids', async () => {
function App() {
return (
<>
@@ -553,8 +553,8 @@ describe('ReactFlight', () => {
const transport = ReactNoopFlightServer.render(
, {
identifierPrefix: 'foo',
});
- act(() => {
- ReactNoop.render(ReactNoopFlightClient.read(transport));
+ await act(async () => {
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
});
expect(ReactNoop).toMatchRenderedOutput(
<>
@@ -591,8 +591,8 @@ describe('ReactFlight', () => {
const transport = ReactNoopFlightServer.render(
);
expect(Scheduler).toHaveYielded([]);
- act(() => {
- ReactNoop.render(ReactNoopFlightClient.read(transport));
+ await act(async () => {
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
});
expect(Scheduler).toHaveYielded(['ClientDoubler']);
@@ -607,7 +607,7 @@ describe('ReactFlight', () => {
describe('ServerContext', () => {
// @gate enableServerContext
- it('supports basic createServerContext usage', () => {
+ it('supports basic createServerContext usage', async () => {
const ServerContext = React.createServerContext(
'ServerContext',
'hello from server',
@@ -618,17 +618,17 @@ describe('ReactFlight', () => {
}
const transport = ReactNoopFlightServer.render(
);
- act(() => {
+ await act(async () => {
ServerContext._currentRenderer = null;
ServerContext._currentRenderer2 = null;
- ReactNoop.render(ReactNoopFlightClient.read(transport));
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
});
expect(ReactNoop).toMatchRenderedOutput(
hello from server
);
});
// @gate enableServerContext
- it('propagates ServerContext providers in flight', () => {
+ it('propagates ServerContext providers in flight', async () => {
const ServerContext = React.createServerContext(
'ServerContext',
'default',
@@ -649,10 +649,10 @@ describe('ReactFlight', () => {
}
const transport = ReactNoopFlightServer.render(
);
- act(() => {
+ await act(async () => {
ServerContext._currentRenderer = null;
ServerContext._currentRenderer2 = null;
- ReactNoop.render(ReactNoopFlightClient.read(transport));
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
});
expect(ReactNoop).toMatchRenderedOutput(
hi this is server
);
@@ -693,7 +693,7 @@ describe('ReactFlight', () => {
});
// @gate enableServerContext
- it('propagates ServerContext and cleansup providers in flight', () => {
+ it('propagates ServerContext and cleansup providers in flight', async () => {
const ServerContext = React.createServerContext(
'ServerContext',
'default',
@@ -724,8 +724,8 @@ describe('ReactFlight', () => {
}
const transport = ReactNoopFlightServer.render(
);
- act(() => {
- ReactNoop.render(ReactNoopFlightClient.read(transport));
+ await act(async () => {
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
});
expect(ReactNoop).toMatchRenderedOutput(
@@ -788,10 +788,10 @@ describe('ReactFlight', () => {
expect(Scheduler).toHaveYielded(['rendered']);
- act(() => {
+ await act(async () => {
ServerContext._currentRenderer = null;
ServerContext._currentRenderer2 = null;
- ReactNoop.render(ReactNoopFlightClient.read(transport));
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
});
expect(ReactNoop).toMatchRenderedOutput(
hi this is server
);
@@ -828,10 +828,10 @@ describe('ReactFlight', () => {
expect(Scheduler).toHaveYielded([]);
- act(() => {
+ await act(async () => {
ServerContext._currentRenderer = null;
ServerContext._currentRenderer2 = null;
- const flightModel = ReactNoopFlightClient.read(transport);
+ const flightModel = await ReactNoopFlightClient.read(transport);
ReactNoop.render(flightModel.foo);
});
@@ -856,8 +856,8 @@ describe('ReactFlight', () => {
context: [['ServerContext', 'Override']],
});
- act(() => {
- const flightModel = ReactNoopFlightClient.read(transport);
+ await act(async () => {
+ const flightModel = await ReactNoopFlightClient.read(transport);
ReactNoop.render(flightModel);
});
expect(ReactNoop).toMatchRenderedOutput(
Override );
@@ -937,8 +937,8 @@ describe('ReactFlight', () => {
act = require('jest-react').act;
Scheduler = require('scheduler');
- act(() => {
- const serverModel = ReactNoopFlightClient.read(transport);
+ await act(async () => {
+ const serverModel = await ReactNoopFlightClient.read(transport);
ReactNoop.render(
);
});
diff --git a/packages/react-noop-renderer/src/ReactNoopFlightClient.js b/packages/react-noop-renderer/src/ReactNoopFlightClient.js
index 52af83c5ef62a..dd19b702b9ce8 100644
--- a/packages/react-noop-renderer/src/ReactNoopFlightClient.js
+++ b/packages/react-noop-renderer/src/ReactNoopFlightClient.js
@@ -20,7 +20,7 @@ import ReactFlightClient from 'react-client/flight';
type Source = Array
;
-const {createResponse, processStringChunk, close} = ReactFlightClient({
+const {createResponse, processStringChunk, getRoot, close} = ReactFlightClient({
supportsBinaryStreams: false,
resolveModuleReference(bundlerConfig: null, idx: string) {
return idx;
@@ -34,13 +34,13 @@ const {createResponse, processStringChunk, close} = ReactFlightClient({
},
});
-function read(source: Source): T {
+function read(source: Source): Thenable {
const response = createResponse(source, null);
for (let i = 0; i < source.length; i++) {
processStringChunk(response, source[i], 0);
}
close(response);
- return response.readRoot();
+ return getRoot(response);
}
export {read};
diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayClient.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayClient.js
index ece3124bcf80e..ecee74598c579 100644
--- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayClient.js
+++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayClient.js
@@ -18,9 +18,10 @@ import {
resolveSymbol,
resolveError,
close,
+ getRoot,
} from 'react-client/src/ReactFlightClient';
-export {createResponse, close};
+export {createResponse, close, getRoot};
export function resolveRow(response: Response, chunk: RowEncoding): void {
if (chunk[0] === 'J') {
diff --git a/packages/react-server-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js b/packages/react-server-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js
index 0444add037907..4f762230d9ee9 100644
--- a/packages/react-server-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js
+++ b/packages/react-server-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js
@@ -31,13 +31,22 @@ describe('ReactFlightDOMRelay', () => {
});
function readThrough(data) {
- const response = ReactDOMFlightRelayClient.createResponse(null);
+ const response = ReactDOMFlightRelayClient.createResponse();
for (let i = 0; i < data.length; i++) {
const chunk = data[i];
ReactDOMFlightRelayClient.resolveRow(response, chunk);
}
ReactDOMFlightRelayClient.close(response);
- const model = response.readRoot();
+ const promise = ReactDOMFlightRelayClient.getRoot(response);
+ let model;
+ let error;
+ promise.then(
+ m => (model = m),
+ e => (error = e),
+ );
+ if (error) {
+ throw error;
+ }
return model;
}
diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMClient.js b/packages/react-server-dom-webpack/src/ReactFlightDOMClient.js
index 32f8e794f4f70..d2c2dc77a8db2 100644
--- a/packages/react-server-dom-webpack/src/ReactFlightDOMClient.js
+++ b/packages/react-server-dom-webpack/src/ReactFlightDOMClient.js
@@ -7,12 +7,15 @@
* @flow
*/
+import type {Thenable} from 'shared/ReactTypes.js';
+
import type {Response as FlightResponse} from 'react-client/src/ReactFlightClientStream';
import type {BundlerConfig} from './ReactFlightClientWebpackBundlerConfig';
import {
createResponse,
+ getRoot,
reportGlobalError,
processStringChunk,
processBinaryChunk,
@@ -49,21 +52,21 @@ function startReadingFromStream(
.catch(error);
}
-function createFromReadableStream(
+function createFromReadableStream(
stream: ReadableStream,
options?: Options,
-): FlightResponse {
+): Thenable {
const response: FlightResponse = createResponse(
options && options.moduleMap ? options.moduleMap : null,
);
startReadingFromStream(response, stream);
- return response;
+ return getRoot(response);
}
-function createFromFetch(
+function createFromFetch(
promiseForResponse: Promise,
options?: Options,
-): FlightResponse {
+): Thenable {
const response: FlightResponse = createResponse(
options && options.moduleMap ? options.moduleMap : null,
);
@@ -75,13 +78,13 @@ function createFromFetch(
reportGlobalError(response, e);
},
);
- return response;
+ return getRoot(response);
}
-function createFromXHR(
+function createFromXHR(
request: XMLHttpRequest,
options?: Options,
-): FlightResponse {
+): Thenable {
const response: FlightResponse = createResponse(
options && options.moduleMap ? options.moduleMap : null,
);
@@ -103,7 +106,7 @@ function createFromXHR(
request.addEventListener('error', error);
request.addEventListener('abort', error);
request.addEventListener('timeout', error);
- return response;
+ return getRoot(response);
}
export {createFromXHR, createFromFetch, createFromReadableStream};
diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js
index 9de3843764152..f8948c3604a58 100644
--- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js
+++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js
@@ -18,6 +18,7 @@ global.TextDecoder = require('util').TextDecoder;
global.setImmediate = cb => cb();
let act;
+let use;
let clientExports;
let clientModuleError;
let webpackMap;
@@ -40,6 +41,7 @@ describe('ReactFlightDOM', () => {
Stream = require('stream');
React = require('react');
+ use = React.experimental_use;
Suspense = React.Suspense;
ReactDOMClient = require('react-dom/client');
ReactServerDOMWriter = require('react-server-dom-webpack/writer.node.server');
@@ -80,20 +82,6 @@ describe('ReactFlightDOM', () => {
};
}
- async function waitForSuspense(fn) {
- while (true) {
- try {
- return fn();
- } catch (promise) {
- if (typeof promise.then === 'function') {
- await promise;
- } else {
- throw promise;
- }
- }
- }
- }
-
const theInfinitePromise = new Promise(() => {});
function InfiniteSuspend() {
throw theInfinitePromise;
@@ -126,19 +114,18 @@ describe('ReactFlightDOM', () => {
);
pipe(writable);
const response = ReactServerDOMReader.createFromReadableStream(readable);
- await waitForSuspense(() => {
- const model = response.readRoot();
- expect(model).toEqual({
- html: (
-
- hello
- world
-
- ),
- });
+ const model = await response;
+ expect(model).toEqual({
+ html: (
+
+ hello
+ world
+
+ ),
});
});
+ // @gate enableUseHook
it('should resolve the root', async () => {
// Model
function Text({children}) {
@@ -160,7 +147,7 @@ describe('ReactFlightDOM', () => {
// View
function Message({response}) {
- return {response.readRoot().html} ;
+ return ;
}
function App({response}) {
return (
@@ -188,6 +175,7 @@ describe('ReactFlightDOM', () => {
);
});
+ // @gate enableUseHook
it('should not get confused by $', async () => {
// Model
function RootModel() {
@@ -196,7 +184,7 @@ describe('ReactFlightDOM', () => {
// View
function Message({response}) {
- return {response.readRoot().text}
;
+ return {use(response).text}
;
}
function App({response}) {
return (
@@ -222,6 +210,7 @@ describe('ReactFlightDOM', () => {
expect(container.innerHTML).toBe('$1
');
});
+ // @gate enableUseHook
it('should not get confused by @', async () => {
// Model
function RootModel() {
@@ -230,7 +219,7 @@ describe('ReactFlightDOM', () => {
// View
function Message({response}) {
- return {response.readRoot().text}
;
+ return {use(response).text}
;
}
function App({response}) {
return (
@@ -256,6 +245,7 @@ describe('ReactFlightDOM', () => {
expect(container.innerHTML).toBe('@div
');
});
+ // @gate enableUseHook
it('should unwrap async module references', async () => {
const AsyncModule = Promise.resolve(function AsyncModule({text}) {
return 'Async: ' + text;
@@ -266,7 +256,7 @@ describe('ReactFlightDOM', () => {
});
function Print({response}) {
- return {response.readRoot()}
;
+ return {use(response)}
;
}
function App({response}) {
@@ -296,6 +286,7 @@ describe('ReactFlightDOM', () => {
expect(container.innerHTML).toBe('Async: Module
');
});
+ // @gate enableUseHook
it('should be able to import a name called "then"', async () => {
const thenExports = {
then: function then() {
@@ -304,7 +295,7 @@ describe('ReactFlightDOM', () => {
};
function Print({response}) {
- return {response.readRoot()}
;
+ return {use(response)}
;
}
function App({response}) {
@@ -333,6 +324,7 @@ describe('ReactFlightDOM', () => {
expect(container.innerHTML).toBe('and then
');
});
+ // @gate enableUseHook
it('should progressively reveal server components', async () => {
let reportedErrors = [];
@@ -432,7 +424,7 @@ describe('ReactFlightDOM', () => {
};
function ProfilePage({response}) {
- return response.readRoot().rootContent;
+ return use(response).rootContent;
}
const {writable, readable} = getTestStream();
@@ -520,11 +512,12 @@ describe('ReactFlightDOM', () => {
expect(reportedErrors).toEqual([]);
});
+ // @gate enableUseHook
it('should preserve state of client components on refetch', async () => {
// Client
function Page({response}) {
- return response.readRoot();
+ return use(response);
}
function Input() {
@@ -605,6 +598,7 @@ describe('ReactFlightDOM', () => {
expect(inputB.value).toBe('goodbye');
});
+ // @gate enableUseHook
it('should be able to complete after aborting and throw the reason client-side', async () => {
const reportedErrors = [];
@@ -627,7 +621,7 @@ describe('ReactFlightDOM', () => {
const root = ReactDOMClient.createRoot(container);
function App({res}) {
- return res.readRoot();
+ return use(res);
}
await act(async () => {
@@ -649,6 +643,7 @@ describe('ReactFlightDOM', () => {
expect(reportedErrors).toEqual(['for reasons']);
});
+ // @gate enableUseHook
it('should be able to recover from a direct reference erroring client-side', async () => {
const reportedErrors = [];
@@ -677,7 +672,7 @@ describe('ReactFlightDOM', () => {
const root = ReactDOMClient.createRoot(container);
function App({res}) {
- return res.readRoot();
+ return use(res);
}
await act(async () => {
@@ -694,6 +689,7 @@ describe('ReactFlightDOM', () => {
expect(reportedErrors).toEqual([]);
});
+ // @gate enableUseHook
it('should be able to recover from a direct reference erroring client-side async', async () => {
const reportedErrors = [];
@@ -727,7 +723,7 @@ describe('ReactFlightDOM', () => {
const root = ReactDOMClient.createRoot(container);
function App({res}) {
- return res.readRoot();
+ return use(res);
}
await act(async () => {
@@ -751,6 +747,7 @@ describe('ReactFlightDOM', () => {
expect(reportedErrors).toEqual([]);
});
+ // @gate enableUseHook
it('should be able to recover from a direct reference erroring server-side', async () => {
const reportedErrors = [];
@@ -787,7 +784,7 @@ describe('ReactFlightDOM', () => {
const root = ReactDOMClient.createRoot(container);
function App({res}) {
- return res.readRoot();
+ return use(res);
}
await act(async () => {
diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
index f477bba451a6e..7ac0a2b4aa9d1 100644
--- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
+++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
@@ -43,20 +43,6 @@ describe('ReactFlightDOMBrowser', () => {
use = React.experimental_use;
});
- async function waitForSuspense(fn) {
- while (true) {
- try {
- return fn();
- } catch (promise) {
- if (typeof promise.then === 'function') {
- await promise;
- } else {
- throw promise;
- }
- }
- }
- }
-
async function readResult(stream) {
const reader = stream.getReader();
let result = '';
@@ -121,16 +107,14 @@ describe('ReactFlightDOMBrowser', () => {
const stream = ReactServerDOMWriter.renderToReadableStream( );
const response = ReactServerDOMReader.createFromReadableStream(stream);
- await waitForSuspense(() => {
- const model = response.readRoot();
- expect(model).toEqual({
- html: (
-
- hello
- world
-
- ),
- });
+ const model = await response;
+ expect(model).toEqual({
+ html: (
+
+ hello
+ world
+
+ ),
});
});
@@ -156,19 +140,18 @@ describe('ReactFlightDOMBrowser', () => {
const stream = ReactServerDOMWriter.renderToReadableStream( );
const response = ReactServerDOMReader.createFromReadableStream(stream);
- await waitForSuspense(() => {
- const model = response.readRoot();
- expect(model).toEqual({
- html: (
-
- hello
- world
-
- ),
- });
+ const model = await response;
+ expect(model).toEqual({
+ html: (
+
+ hello
+ world
+
+ ),
});
});
+ // @gate enableUseHook
it('should progressively reveal server components', async () => {
let reportedErrors = [];
@@ -259,7 +242,7 @@ describe('ReactFlightDOMBrowser', () => {
};
function ProfilePage({response}) {
- return response.readRoot().rootContent;
+ return use(response).rootContent;
}
const stream = ReactServerDOMWriter.renderToReadableStream(
@@ -456,6 +439,7 @@ describe('ReactFlightDOMBrowser', () => {
expect(isDone).toBeTruthy();
});
+ // @gate enableUseHook
it('should allow an alternative module mapping to be used for SSR', async () => {
function ClientComponent() {
return Client Component ;
@@ -491,7 +475,7 @@ describe('ReactFlightDOMBrowser', () => {
});
function ClientRoot() {
- return response.readRoot();
+ return use(response);
}
const ssrStream = await ReactDOMServer.renderToReadableStream(
@@ -501,6 +485,7 @@ describe('ReactFlightDOMBrowser', () => {
expect(result).toEqual('Client Component ');
});
+ // @gate enableUseHook
it('should be able to complete after aborting and throw the reason client-side', async () => {
const reportedErrors = [];
@@ -539,7 +524,7 @@ describe('ReactFlightDOMBrowser', () => {
const root = ReactDOMClient.createRoot(container);
function App({res}) {
- return res.readRoot();
+ return use(res);
}
await act(async () => {
@@ -579,7 +564,7 @@ describe('ReactFlightDOMBrowser', () => {
const response = ReactServerDOMReader.createFromReadableStream(stream);
function Client() {
- return response.readRoot();
+ return use(response);
}
const container = document.createElement('div');
@@ -613,7 +598,7 @@ describe('ReactFlightDOMBrowser', () => {
const response = ReactServerDOMReader.createFromReadableStream(stream);
function Client() {
- return response.readRoot();
+ return use(response);
}
const container = document.createElement('div');
@@ -644,7 +629,7 @@ describe('ReactFlightDOMBrowser', () => {
const response = ReactServerDOMReader.createFromReadableStream(stream);
function Client() {
- return response.readRoot();
+ return use(response);
}
const container = document.createElement('div');
@@ -699,7 +684,7 @@ describe('ReactFlightDOMBrowser', () => {
}
function Client() {
- return response.readRoot();
+ return use(response);
}
const container = document.createElement('div');
@@ -733,7 +718,7 @@ describe('ReactFlightDOMBrowser', () => {
const response = ReactServerDOMReader.createFromReadableStream(stream);
function Client() {
- return response.readRoot();
+ return use(response);
}
const container = document.createElement('div');
diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayClient.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayClient.js
index cea39ac6f2642..087b93479d424 100644
--- a/packages/react-server-native-relay/src/ReactFlightNativeRelayClient.js
+++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayClient.js
@@ -18,9 +18,10 @@ import {
resolveSymbol,
resolveError,
close,
+ getRoot,
} from 'react-client/src/ReactFlightClient';
-export {createResponse, close};
+export {createResponse, close, getRoot};
export function resolveRow(response: Response, chunk: RowEncoding): void {
if (chunk[0] === 'J') {
diff --git a/packages/react-server-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js b/packages/react-server-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js
index 0d439154b537f..35a2f6684c7cc 100644
--- a/packages/react-server-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js
+++ b/packages/react-server-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js
@@ -48,7 +48,16 @@ describe('ReactFlightNativeRelay', () => {
ReactNativeFlightRelayClient.resolveRow(response, chunk);
}
ReactNativeFlightRelayClient.close(response);
- const model = response.readRoot();
+ const promise = ReactNativeFlightRelayClient.getRoot(response);
+ let model;
+ let error;
+ promise.then(
+ m => (model = m),
+ e => (error = e),
+ );
+ if (error) {
+ throw error;
+ }
return model;
}