Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing nested Suspense boundaries in Simple Suspense Renderer #334

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function renderToString(vnode, context) {
* @param {Object} [context={}] Initial root context object
* @returns {string} serialized HTML
*/
export function renderToStringAsync(vnode, context) {
export async function renderToStringAsync(vnode, context) {
// Performance optimization: `renderToString` is synchronous and we
// therefore don't execute any effects. To do that we pass an empty
// array to `options._commit` (`__c`). But we can go one step further
Expand Down Expand Up @@ -113,7 +113,18 @@ export function renderToStringAsync(vnode, context) {
);

if (Array.isArray(rendered)) {
return Promise.all(rendered).then((rendered) => rendered.join(''));
let count = 0;
let resolved = rendered;

// Resolving nested Promises with a maximum depth of 25
while (
resolved.some((element) => typeof element.then === 'function') &&
count++ < 25
) {
resolved = (await Promise.all(resolved)).flat();
}

return resolved.join('');
}

return rendered;
Expand Down
38 changes: 37 additions & 1 deletion test/compat/async.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('Async renderToString', () => {
expect(rendered).to.equal(expected);
});

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

expect(rendered).to.equal(expected);
});

it('should render JSX with nested suspense boundaries', async () => {
const {
Suspender: SuspenderOne,
suspended: suspendedOne
} = createSuspender();
const {
Suspender: SuspenderTwo,
suspended: suspendedTwo
} = createSuspender();

const promise = renderToStringAsync(
<ul>
<Suspense fallback={null}>
<SuspenderOne>
<li>one</li>
<Suspense fallback={null}>
<SuspenderTwo>
<li>two</li>
</SuspenderTwo>
</Suspense>
<li>three</li>
</SuspenderOne>
</Suspense>
</ul>
);

const expected = `<ul><li>one</li><li>two</li><li>three</li></ul>`;

suspendedOne.resolve();
suspendedTwo.resolve();

const rendered = await promise;

expect(rendered).to.equal(expected);
});
});
Loading