Open5

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

kosuke itokosuke ito

tl;dv

まだ終わってないです

概要

honoのミドルウェアが便利なので、それをRRv7のアプリでも使用したいというモチベーションとCloudflare Wockersにデプロイするのでその諸々を便利にできそうと思ったので使用してみようということで触ってみることにしました。

https://github.com/yusukebe/hono-react-router-adapter?tab=readme-ov-file

hono-react-router-adapterは、HonoとReact Routerを連携させるためのツール群です。 Viteプラグインとハンドラで構成され、Cloudflare WorkersやNode.jsのようなプラットフォームをサポートします。 Honoアプリを作成するだけで、React Routerアプリに適用される。

という優れもの

hono-react-router-adapter は現在不安定です。 APIは将来予告なしに変更される可能性があります。

本番で使用するのは注意が必要かも

また、この記事の内容は古くなっている可能性があるため公式のリポジトリを必ず参照してください

参考

https://github.com/yusukebe/hono-react-router-adapter?tab=readme-ov-file
https://zenn.dev/achamaro/articles/b50dac4edf9883

kosuke itokosuke ito

  • すべてのレスポンスに 「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
kosuke itokosuke ito

導入していく

  • 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
kosuke itokosuke ito

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"
...
...
kosuke itokosuke ito

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',
  }
}