### React アプリを AI で開発する小さなコツ 2026/02/20 --- ### AI にフロントエンドを書かせると… フロントエンドは **暗黙の条件** が非常に多い - デバイスサイズ(モバイル / タブレット / デスクトップ) - インタラクション状態(hover, focus, active ...) - レイアウト制御(absolute, fixed, sticky ...) - 非同期状態(loading, error, empty, success) --- ### ベストプラクティスが定まりにくい - バックエンドにはクリーンアーキテクチャ等の定番がある - フロントエンドは「状況による」が多い - AI はコンテキストなしだと **場当たり的に** コードを書く --- ### 一貫性のないコードベースが出来上がる AI は指示がなければ毎回違う書き方をする 人間が **設計指針** を持って AI を導く必要がある --- ### 今日話すこと 1. コロケーションを意識する 2. ロジックを集約する 3. UI の実装方針を決める 4. MCP でコンテキストを渡す --- ### 1. コロケーションを意識する `components/` と `hooks/` が肥大化する典型的な構成 ```text src/ components/ UserProfile.tsx, UserPosts.tsx, GreetingCard.tsx ... hooks/ useUser.ts, usePosts.ts, useGreeting.ts ... ``` ↓ 機能単位でまとめる ```text features/ greeting/ index.tsx // コンポーネント use-greeting.ts // 専用の hook constants.ts // 定数 user-profile/ index.tsx use-profile.ts ``` --- ### 1.1 Bulletproof React を小さく採用する - 機能単位で `features/` にまとめるアーキテクチャ - バレルによる re-export はしない - 全てを採用する必要はなく、コロケーションの思想だけ借りる AI に「この `features/` 配下にファイルを作って」と指示できる --- ### 1.2 コンポーネントの粒度は大きく始める - ファットなコンポーネントから始めて、分割が必要になったらする - 最初から細かくしても大抵の場合再利用が望めない - AI は「この部分を切り出して」というリファクタリング指示に従いやすい --- ### 2. ロジックを集約する $$UI = f(State)$$ 条件分岐やデータ取得を **一箇所に集める** --- ### 2.1 コンテナパターン $UI = f(State)$ ── 親でデータを集め、子は描画に専念する ```tsx // 親: ロジックを集約 function UserPage({ id }: { id: string }) { const user = useUser(id); const posts = usePosts(id); const canEdit = user.role === "admin"; return ; } // 子: props を受け取って描画するだけ function UserProfile({ user, posts, canEdit }: Props) { return (

{user.name}

{canEdit && }
); } ``` --- ### 2.1.1 form だけは例外 form はバリデーション等が UI に密結合しているので子に閉じ込める ```tsx // 親: submit ハンドラーだけ渡す function UserEditPage({ id }: { id: string }) { const updateUser = useUpdateUser(id); return updateUser(data)} />; } // 子: スキーマもバリデーションも内部で完結 function UserForm({ onSubmit }: Props) { const form = useForm({ resolver: zodResolver(schema) }); return (
{form.formState.errors.name && ( {form.formState.errors.name.message} )}
); } ``` --- ### 2.2 コンテナパターンの問題 親のデータ取得が **全て完了するまで** 子は何も描画できない ```tsx function UserPage({ id }: { id: string }) { const user = useUser(id); // 50ms で返る const posts = usePosts(id); // 2000ms かかる ← 全体がブロック return ; } ``` --- ### 2.3 Async React $$UI = \textit{await}\; f(\textit{await}\; State)$$ コンポーネント自身がデータを取得し、Suspense で表示を制御する ```tsx function UserPage({ id }: { id: string }) { return (
}> }>
); } ``` Suspense の境界ごとに **独立してローディングが制御** される --- ### 2.4 どちらを選んでも プロジェクトルールに **明示** する ```markdown ## データ取得の方針 - データ取得は必ずページコンポーネントで行い、子には props で渡す - 子コンポーネントでは useEffect によるデータ取得を行わない - フォームのみ、内部で状態管理する(onSubmit を props で受け取る) ``` 混在させると AI は余計に一貫性のないコードを書く --- ### 3. UI の実装 - 対応するデバイスサイズを **最初に決めて制限** する - モバイルファーストで実装し、ブレイクポイントの微調整は人間がやる - UI コンポーネント(Shadcn 等)の組み合わせで完結させる - AI にはコンポーネントの「組み立て」だけを任せる --- ### 3.1 レイアウトの責任を親に持たせる コンポーネント自身にマージンを持たせず、`className` で外から渡す ```tsx // コンポーネント: 見た目だけ定義、margin は持たない function UserCard({ user, className }: Props) { return (

{user.name}

); } // 利用側: 文脈に応じたレイアウトを渡す ``` --- ### 4. MCP でコンテキストを渡す AI はコードは読めるが **画面は見えていない** - **Chrome DevTools MCP** ── DOM やスタイルを参照しながら修正 - **Playwright MCP** ── ログイン後のスクリーンショット取得・E2E テスト - **Figma MCP** ── デザイントークンの抽出・Tailwind 設定への反映 - **Serena** ── コード検索の質向上・トークン量削減 --- ### 4.1 MCP / CLI で開発フローを自動化する 設計指針が明文化されていれば issue → PR まで AI に任せられる - **GitHub MCP / CLI** ── issue → 実装 → PR → レビュー対応 - **GitLab CLI(glab)** ── issue → 実装 → MR 作成 指針がないと AI は毎回違う書き方をするのでレビューコストが増える --- ### まとめ 1. **コロケーション** でファイル構造を整理する 2. **$UI = f(State)$** でロジックの集約パターンを統一する 3. **UI の実装方針** を事前に決めて AI の裁量を制限する 4. **MCP** で AI にコンテキストを渡し、精度を上げる AI が書こうが人間が書こうが、大事なのは **一貫性を保ち続けること**