Middleware JSX Renderer
Le middleware JSX Renderer permet de définir une mise en page pour le rendu JSX avec la fonction c.render(), sans utiliser c.setRenderer(). Il permet aussi d’accéder aux instances de Context dans les composants grâce à useRequestContext().
Importation
import { Hono } from 'hono'
import { jsxRenderer, useRequestContext } from 'hono/jsx-renderer'Utilisation
const app = new Hono()
app.get(
'/page/*',
jsxRenderer(({ children }) => {
return (
<html>
<body>
<header>Menu</header>
<div>{children}</div>
</body>
</html>
)
})
)
app.get('/page/about', (c) => {
return c.render(<h1>About me!</h1>)
})Options
facultatif docType: boolean | string
Si vous ne souhaitez pas ajouter de DOCTYPE au début du HTML, définissez l’option docType sur false.
app.use(
'*',
jsxRenderer(
({ children }) => {
return (
<html>
<body>{children}</body>
</html>
)
},
{ docType: false }
)
)Vous pouvez également spécifier le DOCTYPE.
app.use(
'*',
jsxRenderer(
({ children }) => {
return (
<html>
<body>{children}</body>
</html>
)
},
{
docType:
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
}
)
)facultatif stream: boolean | Record<string, string>
Si vous définissez cette option sur true ou fournissez une valeur Record, le rendu est envoyé sous forme de réponse en flux.
const AsyncComponent = async () => {
await new Promise((r) => setTimeout(r, 1000)) // sleep 1s
return <div>Hi!</div>
}
app.get(
'*',
jsxRenderer(
({ children }) => {
return (
<html>
<body>
<h1>SSR Streaming</h1>
{children}
</body>
</html>
)
},
{ stream: true }
)
)
app.get('/', (c) => {
return c.render(
<Suspense fallback={<div>loading...</div>}>
<AsyncComponent />
</Suspense>
)
})Si l’option vaut true, les en-têtes suivants sont ajoutés :
{
'Transfer-Encoding': 'chunked',
'Content-Type': 'text/html; charset=UTF-8',
'Content-Encoding': 'Identity'
}Vous pouvez personnaliser les valeurs des en-têtes en spécifiant les valeurs du Record.
Options définies par une fonction
Vous pouvez passer une fonction recevant un objet Context à la place d’un objet d’options statique. Cela permet de définir dynamiquement les options selon le contexte de la requête, par exemple les variables d’environnement ou les paramètres de la requête.
app.use(
'*',
jsxRenderer(
({ children }) => {
return (
<html>
<body>{children}</body>
</html>
)
},
(c) => ({
stream: c.req.header('X-Enable-Streaming') === 'true',
})
)
)Par exemple, vous pouvez désactiver le streaming lors de la génération d’un site statique (SSG) avec <Suspense> en utilisant l’utilitaire isSSGContext :
app.use(
'*',
jsxRenderer(
({ children }) => {
return (
<div>
<Suspense fallback={'loading...'}>
<Component />
</Suspense>
</div>
)
},
(c) => ({
stream: !isSSGContext(c),
})
)
)Mises en page imbriquées
Le composant Layout permet d’imbriquer les mises en page.
app.use(
jsxRenderer(({ children }) => {
return (
<html>
<body>{children}</body>
</html>
)
})
)
const blog = new Hono()
blog.use(
jsxRenderer(({ children, Layout }) => {
return (
<Layout>
<nav>Blog Menu</nav>
<div>{children}</div>
</Layout>
)
})
)
app.route('/blog', blog)useRequestContext()
useRequestContext() renvoie une instance de Context.
import { useRequestContext, jsxRenderer } from 'hono/jsx-renderer'
const app = new Hono()
app.use(jsxRenderer())
const RequestUrlBadge: FC = () => {
const c = useRequestContext()
return <b>{c.req.url}</b>
}
app.get('/page/info', (c) => {
return c.render(
<div>
You are accessing: <RequestUrlBadge />
</div>
)
})Attention
Vous ne pouvez pas utiliser useRequestContext() avec l’option JSX precompile de Deno. Utilisez react-jsx :
"compilerOptions": {
"jsx": "precompile",
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
}Étendre ContextRenderer
En définissant ContextRenderer comme ci-dessous, vous pouvez passer du contenu supplémentaire au moteur de rendu. Cela est pratique, par exemple, pour modifier le contenu de la balise head selon la page.
declare module 'hono' {
interface ContextRenderer {
(
content: string | Promise<string>,
props: { title: string }
): Response
}
}
const app = new Hono()
app.get(
'/page/*',
jsxRenderer(({ children, title }) => {
return (
<html>
<head>
<title>{title}</title>
</head>
<body>
<header>Menu</header>
<div>{children}</div>
</body>
</html>
)
})
)
app.get('/page/favorites', (c) => {
return c.render(
<div>
<ul>
<li>Eating sushi</li>
<li>Watching baseball games</li>
</ul>
</div>,
{
title: 'My favorites',
}
)
})