diff --git a/compat/src/suspense.js b/compat/src/suspense.js index 48c8960996..874e4f4ae1 100644 --- a/compat/src/suspense.js +++ b/compat/src/suspense.js @@ -208,18 +208,23 @@ Suspense.prototype.render = function (props, state) { export function lazy(loader) { let prom; - let component; + let component = null; let error; + let resolved; function Lazy(props) { if (!prom) { prom = loader(); prom.then( exports => { - component = exports.default || exports; + if (exports) { + component = exports.default || exports; + } + resolved = true; }, e => { error = e; + resolved = true; } ); } @@ -228,11 +233,11 @@ export function lazy(loader) { throw error; } - if (!component) { + if (!resolved) { throw prom; } - return createElement(component, props); + return component ? createElement(component, props) : null; } Lazy.displayName = 'Lazy'; diff --git a/compat/test/browser/suspense.test.jsx b/compat/test/browser/suspense.test.jsx index 727fab65c7..21ec97bff3 100644 --- a/compat/test/browser/suspense.test.jsx +++ b/compat/test/browser/suspense.test.jsx @@ -34,9 +34,11 @@ class Catcher extends Component { } render(props, state) { - return state.error - ?
Catcher did catch: {state.error.message}
- : props.children; + return state.error ? ( +
Catcher did catch: {state.error.message}
+ ) : ( + props.children + ); } } @@ -104,6 +106,59 @@ describe('suspense', () => { }); }); + it('should handle lazy component that rejects without returning a component', async () => { + const errorSpy = vi.fn(); + let renderCount = 0; + + let resolve; + function fakeImport() { + const p = new Promise((_, reject) => { + resolve = () => { + reject(new Error('import failed')); + return p; + }; + }); + return p; + } + + const SomeComponent = lazy(() => + fakeImport().catch(e => { + console.log('caught', e); + errorSpy(e); + }) + ); + + const App = () => { + renderCount++; + if (renderCount > 5) { + throw new Error('Infinite loop detected!'); + } + + console.log('RENDER COUNT', renderCount); + return ( +
+ loading
}> + + + + ); + }; + + render(, scratch); + rerender(); + + expect(scratch.innerHTML).to.contain('loading'); + + const assert = () => { + rerender(); + + expect(scratch.innerHTML).to.contain('
'); + expect(errorSpy).toHaveBeenCalledOnce; + }; + + resolve().then(assert).catch(assert); + }); + it('should reset hooks of components', () => { /** @type {(v) => void} */ let set; @@ -190,11 +245,13 @@ describe('suspense', () => { }; }, []); - return state - ?
{children}
- :
-

hi

-
; + return state ? ( +
{children}
+ ) : ( +
+

hi

+
+ ); }; render( diff --git a/test/browser/refs.test.jsx b/test/browser/refs.test.jsx index 5b42406b4f..f4ca6e8a84 100644 --- a/test/browser/refs.test.jsx +++ b/test/browser/refs.test.jsx @@ -3,7 +3,7 @@ import { createElement, render, Component, createRef, Fragment } from 'preact'; import { setupScratch, teardown } from '../_util/helpers'; import { vi } from 'vitest'; -// gives call count and argument errors names (otherwise sinon just uses "spy"): +// gives call count and argument errors names (otherwise vitest just uses "spy"): let spy = (name, ...args) => { let spy = vi.fn(...args); spy.displayName = `spy('${name}')`; @@ -241,13 +241,15 @@ describe('refs', () => { it('should correctly set nested child refs', () => { const ref = createRef(); const App = ({ open }) => - open - ?
-
-
- :
-
-
; + open ? ( +
+
+
+ ) : ( +
+
+
+ ); render(, scratch); expect(ref.current).to.not.be.null; @@ -360,8 +362,39 @@ describe('refs', () => { render(props, { phase }) { return ( - {phase === 1 - ?
+ {phase === 1 ? ( +
+
+ r + ? calls.push('adding ref to two') + : calls.push('removing ref from two') + } + > + Element two +
+
+ r + ? calls.push('adding ref to three') + : calls.push('removing ref from three') + } + > + Element three +
+
+ ) : phase === 2 ? ( +
+
+ r + ? calls.push('adding ref to one') + : calls.push('removing ref from one') + } + > + Element one +
+
r @@ -381,39 +414,8 @@ describe('refs', () => { Element three
- : phase === 2 - ?
-
- r - ? calls.push('adding ref to one') - : calls.push('removing ref from one') - } - > - Element one -
-
-
- r - ? calls.push('adding ref to two') - : calls.push('removing ref from two') - } - > - Element two -
-
- r - ? calls.push('adding ref to three') - : calls.push('removing ref from three') - } - > - Element three -
-
-
- : null} +
+ ) : null} ); }