website/islands/Suspens.tsx

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}</>
}