-
Notifications
You must be signed in to change notification settings - Fork 88
Description
What version of HonoX are you using?
0.1.52
What steps can reproduce the bug?
Island async component with Suspense causes a fetch error on the server side because there is no "use client" or something similar equivalent.
The following code is pseudo code (because I already gave up and deleted the code).
some-page.tsx
<Suspense fallback={<Loading />}>
<AsyncComponent />
</Suspense>
islands/async-component.tsx
export async function AsyncComponent() {
const apiResult = await fetch("/api/something"); // throws invalid url error
return (<>{apiResult}</>);
}
(If we pass a full URL to AsyncComponent, it will succeed, but the unwanted/unexpected server-side fetch may result in potential SSRF risks.)
What is the expected behavior?
fetch should not be executed in a server context.
What do you see instead?
Invalid url error on server console.
Additional information
In this case, the island component is expected to run in a client context because this <Suspense> only contains island components. However, <AsyncComponent /> also runs in the SSR server context and maybe just ignores useEffect. Therefore, an unwanted fetch runs in the server context.
However, it is not easy to avoid this, because <Suspense> is also used for streaming server-side fetch.
I also tried to create a wrapper island component like the following, but I can't stop the server-side fetch because <AsyncComponent /> is rendered in the page SSR context.
some-page.tsx
<Island fallback={<Loading />}>
<AsyncComponent />
</Island>islands/island.tsx
export function Island(~~~~) {
if (typeof document === "undefined") return (<>{fallback}</>);
const unwrapped = use(children); // or something (this may not work)
return (<Suspense fallback={fallback}>{unwrapped}</Suspense>);
}Maybe I should have added a server check inside AsyncComponent, but I gave up here and switched to full SSR.
If we simply want to fetch data on the client side and bind it to the UI, it would be much nicer if you could just write it as await fetch() in an async component.
I think honox is not React, so we need something like <SuspenseIsland> component that shows a fallback UI during hydration and while the client-side await is in progress, but does nothing on the server.