Open5
React Router v7にhono-react-router-adapterを導入する作業メモ

tl;dv
まだ終わってないです
概要
honoのミドルウェアが便利なので、それをRRv7のアプリでも使用したいというモチベーションとCloudflare Wockersにデプロイするのでその諸々を便利にできそうと思ったので使用してみようということで触ってみることにしました。
hono-react-router-adapterは、HonoとReact Routerを連携させるためのツール群です。 Viteプラグインとハンドラで構成され、Cloudflare WorkersやNode.jsのようなプラットフォームをサポートします。 Honoアプリを作成するだけで、React Routerアプリに適用される。
という優れもの
hono-react-router-adapter は現在不安定です。 APIは将来予告なしに変更される可能性があります。
本番で使用するのは注意が必要かも
また、この記事の内容は古くなっている可能性があるため公式のリポジトリを必ず参照してください
参考

例
- すべてのレスポンスに 「X-Powered-By 」ヘッダを設定するミドルウェアを追加
- JSONメッセージを返す新しいAPIエンドポイント「/api 」を追加
./server/index.ts
import { Hono } from 'hono'
const app = new Hono()
app.use(async (c, next) => {
await next()
c.header('X-Powered-By', 'React Router and Hono')
})
app.get('/api', (c) => {
return c.json({
message: 'Hello',
})
})
export default app

導入していく
- hono-react-router-adapterとhonoをインストール
pnpm i hono-react-router-adapter hono
- サーバーエントリーポイントの指定 / 設定
vite.config.ts
import serverAdapter from 'hono-react-router-adapter/vite'
export default defineConfig({
plugins: [
// ...
reactRouter(),
serverAdapter({
entry: 'server/index.ts',
}),
],
})
server/index.ts
import { Hono } from 'hono'
const app = new Hono()
//...
export default app

Cloudflare Workers
Cloudflare Workers向けの設定
- Cloudflare WorkersとCloudflare Pagesをサポートするために、
@hono/vite-dev-serverに開発用アダプタを追加する。(WorkersとPagesで共通の設定)
vite.config.ts
...
+ import adapter from "@hono/vite-dev-server/cloudflare";
import serverAdapter from "hono-react-router-adapter/vite";
...
...
export default defineConfig({
...
...
plugins:[
...
...
serverAdapter({
+ adapter, // Add Cloudflare adapter
entry: "server/index.ts",
}),
...
]
})
- 自分の場合は、@hono/vite-dev-server が入っていなかったので追加
pnpm add -D @hono/vite-dev-server
- アプリを Cloudflare Workers にデプロイするには、worker.ts に以下のハンドラを記述する
worker.ts
import handle from 'hono-react-router-adapter/cloudflare-workers'
import * as build from './build/server'
import server from './server/index'
export default handle(build, server)
- wrangler.toml に worker.ts を指定する
name = "hogehoge-app"
main = "./worker.ts"
...
...

Honoのcontextを取得する
HonoのコンテキストはReact Routerのルートで取得できるらしい
例えば、server/index.tsでHonoインスタンスからc.set()で値を渡すことができるらしい
server/index.ts
import { Hono } from 'hono'
const app = new Hono<{
Variables: {
message: string
}
}>()
app.use(async (c, next) => {
c.set('message', 'Hi from Hono')
await next()
})
export default app
React Router routeでは、args.context.hono.contextからコンテキストを取得できる
app/routes/home.tsx
import type { Route } from './+types/home'
export const loader = (args: Route.LoaderArgs) => {
const message = args.context.hono.context.get('message')
return { message }
}
export default function Home({ loaderData }: Route.ComponentProps) {
const { message } = loaderData
return <h1>Message is {message}</h1>
}
型推論を有効にするには、load-context.tsを以下のように設定する
load-context.ts
import type { AppLoadContext } from 'react-router'
import type { Context } from 'hono'
import type { PlatformProxy } from 'wrangler'
type Env = {
Variables: {
message: string
}
}
type Cloudflare = Omit<PlatformProxy, 'dispose'>
declare module 'react-router' {
interface AppLoadContext {
cloudflare: Cloudflare
hono: {
context: Context<Env>
}
extra: string
}
}
type GetLoadContext = (args: {
request: Request
context: {
cloudflare: Cloudflare
hono: { context: Context<Env> }
}
}) => AppLoadContext
export const getLoadContext: GetLoadContext = ({ context }) => {
return {
...context,
extra: 'stuff',
}
}