hono icon indicating copy to clipboard operation
hono copied to clipboard

Never responds html via Suspense

Open Code-Hex opened this issue 1 year ago • 4 comments

What version of Hono are you using?

4.0.1

What runtime/platform is your app running on?

Cloudflare Workers

What steps can reproduce the bug?

  const Layout2: FC = async ({ children }) => {
    return await html`<div>${children}</div>`;
  };

  const Layout: FC = async ({ children }) => {
    // return await html`<div>${Layout2({ children })}</div>`; // Will be successful
    return await html`<div>${await Layout2({ children })}</div>`;
  };

  app
    .use(
      '/suspense',
      jsxRenderer(
         ({ children }) => {
          return (<Layout>{children}</Layout>);
        },
        { stream: true },
      ),
    )
    .get('/suspense', async (c) => {
      const AsyncComponent: FC = async () => {
        await new Promise((r) => setTimeout(r, 1000)); // sleep 1s
        return await (<Layout>Hello</Layout>);
      };
      return await c.render(
        <Suspense fallback={<div>Loading...</div>}>
          <AsyncComponent />
        </Suspense>,
      );
    });

What is the expected behavior?

Render Layout2 component after finished sleep 1s

What do you see instead?

Loading...

Additional information

No response

Code-Hex avatar Feb 14 '24 02:02 Code-Hex

Hi @Code-Hex

I think the await is not necessary:

return await html`<div>${await Layout2({ children })}</div>`

The above should be the following as you write:

return await html`<div>${Layout2({ children })}</div>`

Is there any reason to write await?

yusukebe avatar Feb 16 '24 02:02 yusukebe

@yusukebe I didn't write async await code directly, written it by @typescript-eslint/promise-function-async

related: https://github.com/honojs/hono/issues/1812

This issue is a simplified version to demonstrate reproducibility. In reality, the problem occurred with the following code:

const Layout2 = async ({children}: {children:JSX.Element}) => await html`<div>${children}</div>`

<div>${Layout2({ children: await Layout3() })}</div>

Identifying the problem is difficult because lint automatically corrects it to:

const Layout2 = async ({children}: {children:JSX.Element}) => await html`<div>${children}</div>`

<div>${Layout2({ children: Layout3() })}</div> // will be changed by linter

Code-Hex avatar Feb 16 '24 04:02 Code-Hex

Hi @Code-Hex

Can you adopt the work-around of writing the following?

return await html`<div>${<Layout2>{children}</Layout2>}</div>`

or

import { jsx } from 'hono/jsx'
...
return await html`<div>${jsx(Layout2, {}, children)}</div>`

usualoma avatar Feb 16 '24 05:02 usualoma

@usualoma Thank you. I'm currently avoiding with a similar approach!

return await html`<div>${<Layout2>{children}</Layout2>}</div>`

Code-Hex avatar Feb 16 '24 05:02 Code-Hex

@Code-Hex Thank you for your response.

I thought, "Doesn't this look better?".

return await html`<div>${<Layout2>{children}</Layout2>}</div>`

However, there may be times when it is more convenient to pass HtmlEscapedString, so I would like to support this notation in #2233.

usualoma avatar Feb 17 '24 06:02 usualoma