Skip to content

Commit c57b230

Browse files
author
David Dios
authored
fixing nested Suspense boundaries (#334)
1 parent ad7b662 commit c57b230

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export function renderToString(vnode, context) {
8484
* @param {Object} [context={}] Initial root context object
8585
* @returns {string} serialized HTML
8686
*/
87-
export function renderToStringAsync(vnode, context) {
87+
export async function renderToStringAsync(vnode, context) {
8888
// Performance optimization: `renderToString` is synchronous and we
8989
// therefore don't execute any effects. To do that we pass an empty
9090
// array to `options._commit` (`__c`). But we can go one step further
@@ -113,7 +113,18 @@ export function renderToStringAsync(vnode, context) {
113113
);
114114

115115
if (Array.isArray(rendered)) {
116-
return Promise.all(rendered).then((rendered) => rendered.join(''));
116+
let count = 0;
117+
let resolved = rendered;
118+
119+
// Resolving nested Promises with a maximum depth of 25
120+
while (
121+
resolved.some((element) => typeof element.then === 'function') &&
122+
count++ < 25
123+
) {
124+
resolved = (await Promise.all(resolved)).flat();
125+
}
126+
127+
return resolved.join('');
117128
}
118129

119130
return rendered;

test/compat/async.test.js

+37-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('Async renderToString', () => {
2525
expect(rendered).to.equal(expected);
2626
});
2727

28-
it('should render JSX with nested suspense boundary', async () => {
28+
it('should render JSX with nested suspended components', async () => {
2929
const {
3030
Suspender: SuspenderOne,
3131
suspended: suspendedOne
@@ -58,4 +58,40 @@ describe('Async renderToString', () => {
5858

5959
expect(rendered).to.equal(expected);
6060
});
61+
62+
it('should render JSX with nested suspense boundaries', async () => {
63+
const {
64+
Suspender: SuspenderOne,
65+
suspended: suspendedOne
66+
} = createSuspender();
67+
const {
68+
Suspender: SuspenderTwo,
69+
suspended: suspendedTwo
70+
} = createSuspender();
71+
72+
const promise = renderToStringAsync(
73+
<ul>
74+
<Suspense fallback={null}>
75+
<SuspenderOne>
76+
<li>one</li>
77+
<Suspense fallback={null}>
78+
<SuspenderTwo>
79+
<li>two</li>
80+
</SuspenderTwo>
81+
</Suspense>
82+
<li>three</li>
83+
</SuspenderOne>
84+
</Suspense>
85+
</ul>
86+
);
87+
88+
const expected = `<ul><li>one</li><li>two</li><li>three</li></ul>`;
89+
90+
suspendedOne.resolve();
91+
suspendedTwo.resolve();
92+
93+
const rendered = await promise;
94+
95+
expect(rendered).to.equal(expected);
96+
});
6197
});

0 commit comments

Comments
 (0)