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 9ff119ac1419d..cea35bc1b5c29 100644
--- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js
+++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js
@@ -812,6 +812,105 @@ describe('ReactFlightDOM', () => {
expect(reportedErrors).toEqual([]);
});
+ it('should handle streaming async server components', async () => {
+ const reportedErrors = [];
+
+ const Row = async ({current, next}) => {
+ const chunk = await next;
+
+ if (chunk.done) {
+ return chunk.value;
+ }
+
+ return (
+
+
+
+ );
+ };
+
+ function createResolvablePromise() {
+ let _resolve, _reject;
+
+ const promise = new Promise((resolve, reject) => {
+ _resolve = resolve;
+ _reject = reject;
+ });
+
+ return {promise, resolve: _resolve, reject: _reject};
+ }
+
+ function createSuspendedChunk(initialValue) {
+ const {promise, resolve, reject} = createResolvablePromise();
+
+ return {
+ row: (
+
+
+
+ ),
+ resolve,
+ reject,
+ };
+ }
+
+ function makeDelayedText() {
+ const {promise, resolve, reject} = createResolvablePromise();
+ async function DelayedText() {
+ const data = await promise;
+ return
{data}
;
+ }
+ return [DelayedText, resolve, reject];
+ }
+
+ const [Posts, resolvePostsData] = makeDelayedText();
+ const suspendedChunk = createSuspendedChunk(loading
);
+ const {writable, readable} = getTestStream();
+ const {pipe} = ReactServerDOMServer.renderToPipeableStream(
+ suspendedChunk.row,
+ webpackMap,
+ {
+ onError(error) {
+ reportedErrors.push(error);
+ },
+ },
+ );
+ pipe(writable);
+ const response = ReactServerDOMClient.createFromReadableStream(readable);
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+
+ function ClientRoot() {
+ return use(response);
+ }
+
+ await act(() => {
+ root.render();
+ });
+
+ expect(container.innerHTML).toBe('loading
');
+
+ const donePromise = createResolvablePromise();
+ const value = ;
+
+ await act(async () => {
+ suspendedChunk.resolve({value, done: false, next: donePromise.promise});
+ await Promise.resolve();
+ donePromise.resolve({value, done: true});
+ });
+
+ expect(container.innerHTML).toBe('loading
');
+
+ await act(async () => {
+ jest.advanceTimersByTime(500);
+ await resolvePostsData('posts');
+ await 'the inner async function';
+ });
+
+ expect(container.innerHTML).toBe('posts
');
+ expect(reportedErrors).toEqual([]);
+ });
+
it('should preserve state of client components on refetch', async () => {
// Client
diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js
index d253db44c4a15..a7404833986c0 100644
--- a/packages/react-server/src/ReactFlightServer.js
+++ b/packages/react-server/src/ReactFlightServer.js
@@ -1334,16 +1334,12 @@ function renderModelDestructive(
// but that is able to reuse the same task if we're already in one but then that
// will be a lazy future value rather than guaranteed to exist but maybe that's good.
const newId = outlineModel(request, (value: any));
- return serializeByValueID(newId);
+ return serializeLazyID(newId);
} else {
// We've already emitted this as an outlined object, so we can refer to that by its
- // existing ID. TODO: We should use a lazy reference since, unlike plain objects,
- // elements might suspend so it might not have emitted yet even if we have the ID for
- // it. However, this creates an extra wrapper when it's not needed. We should really
- // detect whether this already was emitted and synchronously available. In that
- // case we can refer to it synchronously and only make it lazy otherwise.
- // We currently don't have a data structure that lets us see that though.
- return serializeByValueID(existingId);
+ // existing ID. We use a lazy reference since, unlike plain objects, elements might
+ // suspend so it might not have emitted yet even if we have the ID for it.
+ return serializeLazyID(existingId);
}
} else {
// This is the first time we've seen this object. We may never see it again