Skip to content

Commit

Permalink
Fix rendering waterfall of suspended components in a single Suspense …
Browse files Browse the repository at this point in the history
…boundary (#413)
  • Loading branch information
f0x52 authored Jan 19, 2025
1 parent 81e7da3 commit 27f340b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changeset/long-ties-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'preact-render-to-string': patch
---

Fix async rendering of multiple suspended components in a single Suspense boundary
17 changes: 2 additions & 15 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
};

Expand Down
42 changes: 42 additions & 0 deletions test/compat/async.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<ul>
<Suspense fallback={null}>
<SuspenderOne>
<li>one</li>
</SuspenderOne>
<SuspenderTwo>
<li>two</li>
</SuspenderTwo>
<SuspenderThree>
<li>three</li>
</SuspenderThree>
</Suspense>
</ul>
);

const expected = `<ul><!--$s--><li>one</li><!--/$s--><!--$s--><li>two</li><!--/$s--><!--$s--><li>three</li><!--/$s--></ul>`;

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();

Expand Down

0 comments on commit 27f340b

Please sign in to comment.