54 lines
1.1 KiB
TypeScript
54 lines
1.1 KiB
TypeScript
// import { Suspense } from '@univoq // Error - mismatch in imports version
|
|
import { JSX } from 'preact'
|
|
import { useSignal } from '@preact/signals'
|
|
|
|
function RenderError(
|
|
{ error, fallback }: { error: Error; fallback: Fallback | undefined },
|
|
) {
|
|
if (fallback) {
|
|
return fallback({ error })
|
|
}
|
|
|
|
return (
|
|
<output>
|
|
<pre>{String(error)}</pre>
|
|
</output>
|
|
)
|
|
}
|
|
|
|
export type Fallback = ({ error }: { error: Error }) => JSX.Element
|
|
|
|
export default function Suspense(
|
|
{ loader, fallback, signal, children }: {
|
|
loader: JSX.Element
|
|
fallback?: Fallback
|
|
signal?: AbortSignal
|
|
children: Promise<JSX.Element>
|
|
},
|
|
) {
|
|
const displayed = useSignal(loader)
|
|
let loaded = false
|
|
|
|
signal?.addEventListener('abort', () => {
|
|
if (loaded) return
|
|
try {
|
|
signal.throwIfAborted()
|
|
} catch (error) {
|
|
displayed.value = RenderError({ error, fallback })
|
|
}
|
|
})
|
|
|
|
children
|
|
.then((element) => {
|
|
if (signal?.aborted) return
|
|
displayed.value = element
|
|
loaded = true
|
|
})
|
|
.catch((error) => {
|
|
if (signal?.aborted) return
|
|
displayed.value = RenderError({ error, fallback })
|
|
})
|
|
|
|
return <>{displayed}</>
|
|
}
|