第2回
最小Reactフレームワーク Waku をCloudflareで動かす
Jotai作者による最小Reactフレームワーク Waku を実際に試す。プロジェクト作成からServer Components、Cloudflareへのデプロイまでを手順を追って動かしながら解説する。
制作・運営:React Guide 運営者
教材の範囲:以下は別プロジェクトで試す手順とコード例です。記事内の見本は静的な説明用表示です。導入先のフレームワーク・外部サービスまでの実行確認は含めていません。
「React最新ラボ」第2回。今回はフレームワークを丸ごと立ち上げて動かす。Server Components(RSC)を一番小さい構成で試し、最後に Cloudflare へデプロイする。
導入時は生成されたpackage.jsonとロックファイルで使用版を記録してください。現行の公式ドキュメントのNode.js要件は22.15以上の22系、24系、26系です。
そもそも RSC(Server Components)で何が嬉しい?
これまでの作り方とRSCの違いを並べると分かりやすい。
| これまで(CSR) | RSC | |
|---|---|---|
| データ取得 | useEffect + fetch をクライアントで実行 |
コンポーネント自体が async。サーバで取得 |
| クライアントに届くもの | コンポーネントのJS+取得後のデータ | RSCの出力データ。初回のHTML生成はSSRと組み合わせる |
| 秘密情報 | ブラウザへ渡さずサーバーで扱う | サーバーの環境変数で管理し、props・返り値へ含めない |
プロジェクトを作る
Cloudflare に置く前提なら、Cloudflare のCLI(C3)から作るのが速い。
npm create cloudflare@latest my-waku-app -- --framework=waku対話では次のように選ぶ。
- テンプレート:Framework Starter → Waku
- git で管理:Yes
- すぐデプロイ:No(まずローカルで動かす)
Cloudflare 抜きで純粋に Waku だけ試すなら
npm create waku@latestでもいい。
作ったら開発サーバを起動する。
cd my-waku-app
npm run dev
# → http://localhost:3000ファイル構成:src/pages がそのままルートになる
Waku は src/pages ディレクトリのファイルベースルーティング。ファイルを置けば、そのままURLになる。
src/
├── pages/
│ ├── _layout.tsx # 全ページ共通のレイアウト
│ ├── index.tsx # /
│ └── about.tsx # /about
├── components/
└── waku.server.ts # サーバ/ルーティング設定(生成構成によって異なる)各ページは「default で React コンポーネントを export」し、必要に応じて「getConfig という名前付き関数を export」してレンダリング方法を指定する。
サーバコンポーネントを書く(デフォルトでサーバ実行)
src/pages 以下のコンポーネントは、何も書かなければサーバコンポーネント。async にできて、中で直接 fetch やDBアクセスを書ける。
// ./src/pages/index.tsx
export default async function HomePage() {
// ここはサーバで動く。fetch も DB も直接書ける
const res = await fetch('https://api.github.com/repos/wakujs/waku')
if (!res.ok) throw new Error(`GitHub API: HTTP ${res.status}`)
const repo = await res.json()
return (
<main>
<h1>Waku を試す</h1>
<p>GitHub ★ {repo.stargazers_count}</p>
</main>
)
}
// レンダリング方法を指定(dynamic = リクエストごとにサーバでレンダ)
export const getConfig = async () => {
return { render: 'dynamic' } as const
}render: 'static' にすれば、ビルド時にHTMLを生成する(静的化/SSG)。ブログのように内容が変わらないページに向く。
Waku を試す
GitHub ★ 13200
インタラクティブな所だけ 'use client'
クリックや useState が必要な部分は、クライアントコンポーネントに切り出す。ファイルの先頭に 'use client' を書くだけ。
// ./src/components/counter.tsx
'use client'
import { useState } from 'react'
export const Counter = () => {
const [count, setCount] = useState(0)
return <button onClick={() => setCount((c) => c + 1)}>カウント: {count}</button>
}これをサーバコンポーネントから普通に import して置けばいい。「ページ全体はサーバ、ボタンだけクライアント」という構成になる。
サーバ処理を呼ぶ:'use server'(Server Functions)
フォーム送信やDB書き込みなど「サーバでだけ動かしたい処理」は Server Function にする。専用ファイルの先頭に 'use server' を置く。
// ./src/actions/send-message.ts
'use server'
export async function sendMessage(message: string) {
// ここはサーバでだけ実行される。DB接続などをここに書く
console.log('received:', message)
}// ./src/components/contact-button.tsx
'use client'
import { sendMessage } from '../actions/send-message'
export const ContactButton = () => {
return <button onClick={() => sendMessage('Hello!')}>送信</button>
}'use server' のファイルから export した関数は、そのままサーバのエンドポイントになる。クライアント側からは「ただの関数呼び出し」に見えるのに、中身はサーバで動く。
Cloudflareに公開する前の確認
CloudflareのC3で作ったプロジェクトでは、生成されたpackage.jsonのscriptsとCloudflare設定を使う。異なるWakuの版向けのアダプタを上書きしない。
npm run build
npm run deploydeployはCloudflareのアカウントへ公開する操作。先にローカルでページ・カウンター・HTTPエラー時の表示を確認し、生成先とアカウントを確認して実行する。
Cloudflare公式のWakuガイドの現行手順を基準にする。当記事の見本は公開成功の証拠ではなく、今回の見直しではこのフレームワークの外部デプロイまでは行っていない。
Server Functionsは外部から呼べる入口になる。TypeScriptの引数型だけでは受信データの検証にも認証にもならない。保存処理を追加するときは、サーバー側で入力と操作権限を検証する。
つまずきやすい所
- サーバとクライアントの境界:
windowやlocalStorageなどブラウザ専用APIはサーバコンポーネントで使えない。使う部分は'use client'側へ移す。 - 使用版の確認:バージョンによってAPIや設定の細部が変わることがある。詰まったら必ず公式ドキュメントで現行の書き方を確認する。
まとめ
- Waku は Vite + Hono ベースの最小RSCフレームワーク。
src/pagesのファイルベースルーティング - 何も書かなければサーバコンポーネント。
asyncでデータ取得をそのまま書ける - インタラクティブは
'use client'、サーバ処理は'use server'で切り分ける - Cloudflare向けの生成設定を確認し、ローカルビルド後に公開する
次回は第3回。見た目の話に戻って、アニメ付きUIコンポーネント集 React Bits を、このサイトと同じ Vite + Tailwind 環境に最短で入れて動かす。
確認してみる
サーバー取得を失敗させたときの表示と、Counterがクリックで増えることを別々に確認する。ブラウザへ送られるpropsに秘密情報を含めない。
同じテーマの教材
Cloudflare Pagesにデプロイ:作ったWebサイトを公開する
npm run buildでReactアプリをビルドし、Cloudflare PagesにGitHubリポジトリを接続して自動デプロイを設定する。URLが生まれるまでの全手順。
useState・useRef・useReducerの型定義:TypeScriptで正しく型をつける
TypeScriptでuseState・useRef・useReducerに型をつける方法を解説。型推論が効くケースと明示が必要なケース、useRefのnull初期値問題まで実例付きで説明します。
Reactイベントハンドラーの型定義:onClick・onChange・onSubmit
TypeScriptでReactのイベントハンドラーを正しく型定義する方法を解説。onClick・onChange・onSubmit・フォーム要素ごとの型からpropsでの受け渡しまで実例付きで説明します。