honojs / honox

HonoX - Hono based meta framework
https://hono.dev
MIT License
1.64k stars 43 forks source link

Islands component that accepts children #47

Closed berlysia closed 8 months ago

berlysia commented 8 months ago

What is the feature you are proposing?

Current islands component can not render children in the client side.

If we were achieve this, it means we have "Donut Components" pattern.

One of the reasons is that the child is not passed in this part, but in addition to this, it seems necessary to modify the hydrate process when an island exists within an island.

yusukebe commented 8 months ago

Hi @berlysia

This issue is resolved by #35. I'll release the new version, including the change, later.

usualoma commented 8 months ago

Hi @berlysia Thanks for the report! cc @yusukebe

35 will partially fix this problem, but it does not work correctly when children is a function component. I have an idea to deal with this, so please give me a few days.

usualoma commented 8 months ago

Support children with server-side Suspense (streaming) is coming soon.

https://github.com/honojs/honox/assets/30598/3103eefb-2e28-4a98-99de-da8c9dfe850c

const ChildContent3 = async () => {
  await new Promise((resolve) => setTimeout(resolve, 1000))
  throw new Error('Error in child content 3')
}

const ChildContent2 = async () => {
  await new Promise((resolve) => setTimeout(resolve, 1000))
  return (
    <>
      <p>Counter children 2</p>
      <ErrorBoundary fallback={<p>Something went wrong</p>}>
        <Suspense fallback="<p>Loading...</p>">
          <ChildContent3 />
        </Suspense>
      </ErrorBoundary>
    </>
  )
}

const ChildContent1 = async () => {
  await new Promise((resolve) => setTimeout(resolve, 1000))
  return (
    <>
      <p>Counter children 1</p>
      <Suspense fallback="<p>Loading...</p>">
        <ChildContent2 />
      </Suspense>
    </>
  )
}

const Children = async () => {
  return (
    <Suspense fallback="<p>Loading...</p>">
      <ChildContent1 />
    </Suspense>
  )
}

export default createRoute((c) => {
  const name = c.req.query('name') ?? 'no name'
  return c.render(
    <div class={className}>
      <h1>Hello, {name}!</h1>
      <Counter>
        <Children />
      </Counter>
    </div>,
    { title: name }
  )
})
yusukebe commented 8 months ago

@berlysia !

I've released v0.1.2, which supports that it can pass children to islands/client components (including Suspense!) thanks to @usualoma . Could you try it?

usualoma commented 8 months ago

Oops, forgot to get the attribute values! I'll fix that right away.

berlysia commented 8 months ago

Great. The fix works for my site locally. I'm waiting for merge and publish.