diff --git a/.changeset/heavy-sloths-cry.md b/.changeset/heavy-sloths-cry.md new file mode 100644 index 00000000..1f8c6fa7 --- /dev/null +++ b/.changeset/heavy-sloths-cry.md @@ -0,0 +1,5 @@ +--- +'preact-render-to-string': patch +--- + +Ensure that in streaming mode throwing a suspension multiple times from the same component works diff --git a/src/index.js b/src/index.js index 0d83e3bf..f1bf5b93 100644 --- a/src/index.js +++ b/src/index.js @@ -380,11 +380,12 @@ function _renderToString( try { rendered = type.call(component, props, cctx); - } catch (e) { - if (asyncMode) { + } catch (error) { + if (asyncMode && error && typeof error.then == 'function') { vnode._suspended = true; } - throw e; + + throw error; } } @@ -508,17 +509,24 @@ function _renderToString( return str; } catch (error) { if (!asyncMode && renderer && renderer.onError) { - let res = renderer.onError(error, vnode, (child, parent) => - _renderToString( - child, - context, - isSvgMode, - selectValue, - parent, - asyncMode, - renderer - ) - ); + const onError = (error) => { + return renderer.onError(error, vnode, (child, parent) => { + try { + return _renderToString( + child, + context, + isSvgMode, + selectValue, + parent, + asyncMode, + renderer + ); + } catch (e) { + return onError(e); + } + }); + }; + let res = onError(error); if (res !== undefined) return res; diff --git a/test/compat/render-chunked.test.jsx b/test/compat/render-chunked.test.jsx index 51b93160..3f20e517 100644 --- a/test/compat/render-chunked.test.jsx +++ b/test/compat/render-chunked.test.jsx @@ -190,4 +190,38 @@ describe('renderToChunks', () => { '' ]); }); + + it('should support a component that suspends multiple times', async () => { + const { Suspender, suspended } = createSuspender(); + const { Suspender: Suspender2, suspended: suspended2 } = createSuspender(); + + function MultiSuspender() { + return ( + + + + + ); + } + + const result = []; + const promise = renderToChunks( +
+ +
, + { onWrite: (s) => result.push(s) } + ); + + suspended.resolve(); + suspended2.resolve(); + await promise; + + expect(result).to.deep.equal([ + '
loading part 1...
', + '' + ]); + }); });