Suspense doesn't work as expected when throwing promises multiple times. In such case(s) content gets duplicated - it seems that the old component disconnects from its parent aand stays in the dom.
This is not happening with React's Suspense.
Example of the problem in Preact:
https://codesandbox.io/s/amazing-jennings-6b21f?file=/src/index.jsx
The same example in React: (elements don't get duplicated)
https://codesandbox.io/s/prod-shape-345d2?file=/src/App.jsx
Throw a promise multiple times from a component inside of a Suspense.
import { Suspense, useState } from "preact/compat";
import { Component, render } from "preact";
export default class App extends Component {
render(props, { results = [] }) {
return (
<Suspense fallback="loading ...">
<MyWrapper />
</Suspense>
);
}
}
if (typeof window !== "undefined") {
render(<App />, document.getElementById("root"));
}
function MyWrapper() {
const [refreshState, onRefreshStateChange] = useState(0);
const forceRefresh = () => onRefreshStateChange(refreshState + 1);
return <MyAsyncComponent forceRefresh={forceRefresh} />;
}
let uglyCache = null;
function MyAsyncComponent(props) {
if (uglyCache) {
const tmp = uglyCache;
uglyCache = null;
return tmp;
}
const asyncLoad = async () => {
await artificialDelay();
const result = (
<div>
My happy component
<button onClick={props.forceRefresh}>click me</button>
</div>
);
uglyCache = result;
};
throw asyncLoad();
}
async function artificialDelay(time) {
return new Promise(resolve => {
setTimeout(() => resolve(), time);
});
}
Content will not be duplicated.
Content gets duplicated on each throw promise after the first one.
The issue might be related to #2504 and #2488.
Something goes crazy with unmounting/parking the old vnodes. When wrapping the <MyWrapper /> in a <div> it is at least unmounted while the fallback is rendered but the old DOM node is inserted again after the suspension clears. I'll try to dig into this the following days.
Btw. seems we have a test for this case but it doesn't work 馃槗