From 27f340b6e7d77ec7775a49a78d105cad26fa0857 Mon Sep 17 00:00:00 2001 From: f0x52 Date: Sun, 19 Jan 2025 18:19:49 +0100 Subject: [PATCH] Fix rendering waterfall of suspended components in a single Suspense boundary (#413) --- .changeset/long-ties-brake.md | 5 +++++ src/index.js | 17 ++------------ test/compat/async.test.jsx | 42 +++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 .changeset/long-ties-brake.md diff --git a/.changeset/long-ties-brake.md b/.changeset/long-ties-brake.md new file mode 100644 index 0000000..333694f --- /dev/null +++ b/.changeset/long-ties-brake.md @@ -0,0 +1,5 @@ +--- +'preact-render-to-string': patch +--- + +Fix async rendering of multiple suspended components in a single Suspense boundary diff --git a/src/index.js b/src/index.js index f6530f4..9c69683 100644 --- a/src/index.js +++ b/src/index.js @@ -542,21 +542,8 @@ function _renderToString( : result; } catch (e) { if (!e || typeof e.then != 'function') throw e; - - return e.then(() => { - const result = _renderToString( - rendered, - context, - isSvgMode, - selectValue, - vnode, - asyncMode, - renderer - ); - return vnode._suspended - ? BEGIN_SUSPENSE_DENOMINATOR + result + END_SUSPENSE_DENOMINATOR - : result; - }, renderNestedChildren); + + return e.then(renderNestedChildren); } }; diff --git a/test/compat/async.test.jsx b/test/compat/async.test.jsx index 0ca7835..62a347d 100644 --- a/test/compat/async.test.jsx +++ b/test/compat/async.test.jsx @@ -254,6 +254,48 @@ describe('Async renderToString', () => { expect(rendered).to.equal(expected); }); + it('should render JSX with multiple suspended direct children within a single suspense boundary that resolve one-after-another', async () => { + const { + Suspender: SuspenderOne, + suspended: suspendedOne + } = createSuspender(); + const { + Suspender: SuspenderTwo, + suspended: suspendedTwo + } = createSuspender(); + const { + Suspender: SuspenderThree, + suspended: suspendedThree + } = createSuspender(); + + const promise = renderToStringAsync( + + ); + + const expected = ``; + + suspendedOne.promise.then(() => { void suspendedTwo.resolve();}); + suspendedTwo.promise.then(() => { void suspendedThree.resolve();}); + + suspendedOne.resolve(); + + const rendered = await promise; + + expect(rendered).to.equal(expected); + }); + it('should rethrow error thrown after suspending', async () => { const { suspended, getResolved } = createSuspender();