Reactコンポーネント設計の基礎
Reactのコンポーネント概念・Props・JSXを理解し、UIを再利用可能な部品として構築する考え方を習得する
Reactはユーザーインターフェースを「コンポーネント」という再利用可能な部品に分割して構築するライブラリである。「UIは状態の関数である(UI = f(state))」という考え方が核心で、状態が変わると自動的にUIが更新される。
この章で作るもの(全体像)
基礎編を貫く Todo アプリの第 1 ステップとして、App → TodoList → TodoItem のツリーを props だけで作る。
コンポーネントの基本
// シンプルなコンポーネント
export function Greeting() {
return <h1>こんにちは</h1>;
}
// JSX: JavaScript の中に HTML のような構文が書ける
// 実際には React.createElement に変換される
JSXのルール:
- 必ず一つのルート要素を返す(
<>...</>のフラグメントで複数をラップできる) classではなくclassNameを使う(HTMLと同じ属性名にできないため){}でJavaScript式を埋め込める
Props(コンポーネントへの入力)
// Props の型定義
interface PatientCardProps {
name: string;
age: number;
status: "active" | "inactive";
}
// Props を受け取るコンポーネント
export function PatientCard({ name, age, status }: PatientCardProps) {
return (
<div className="card">
<h2>{name}</h2>
<p>年齢: {age}</p>
<span className={status === "active" ? "text-green-500" : "text-gray-400"}>
{status}
</span>
</div>
);
}
// 使用側
<PatientCard name="山田太郎" age={45} status="active" />
Propsはコンポーネントへの引数であり、読み取り専用。コンポーネントはPropsを変更しない(純粋関数の考え方)。
リストのレンダリング
const patients = [
{ id: "1", name: "山田太郎", age: 45 },
{ id: "2", name: "佐藤花子", age: 32 },
];
export function PatientList() {
return (
<ul>
{patients.map((patient) => (
<PatientCard
key={patient.id} // keyは必須(Reactが差分更新に使う)
name={patient.name}
age={patient.age}
status="active"
/>
))}
</ul>
);
}
コンポーネントの分割方針
単一責任原則: 1つのコンポーネントは1つの役割を持つ。大きくなったら分割する目安:
- 100行を超えたら分割を検討
- 同じUIパターンが2箇所以上に現れたら共通化
- 「〇〇カード」「〇〇リスト」「〇〇フォーム」のように役割で命名できるか
Page コンポーネント(ページ全体)
└─ Layout(ヘッダー・サイドバー・フッター)
└─ PatientList(患者一覧)
└─ PatientCard(患者カード)× n
└─ StatusBadge(ステータスバッジ)
Propsのバケツリレー問題
コンポーネントツリーが深くなると、上位から下位へPropsを何段も経由して渡す「Propsのドリリング」が発生する。3階層を超えるようなら状態管理ライブラリ(b12参照)の検討サインとなる。
Vite + React Router v7 のファイル構成
このカリキュラムでは web/src/ 以下に手動でファイルを配置し、React Router v7 でルーティングを定義する:
web/src/
├── main.tsx アプリのエントリーポイント(RouterProvider)
├── router.tsx ルーティング設定(createBrowserRouter)
├── App.tsx 共通レイアウト(Header + Outlet)
├── pages/
│ ├── Home.tsx タイムライン(/)
│ ├── Login.tsx ログイン(/login)
│ └── Profile.tsx プロフィール(/profile/:username)
└── components/
├── PostCard.tsx 投稿カード(再利用可能なUI部品)
└── Header.tsx ヘッダーナビゲーション
pages/ がURL対応のページコンポーネント、components/ が再利用される部品コンポーネント。ルーティングの詳細は b14 を参照。
手を動かす
基礎編を通して育てる Todo アプリ の第 1 ステップ。ここでは props だけで静的な一覧を描画する。
やること
web/ディレクトリで Vite + React + TypeScript のひな型を作るweb/src/components/TodoItem.tsxを作り、textとdoneを props で受け取るweb/src/App.tsxでハードコードした Todo 配列をTodoItemに map で流し込む
プロジェクト構成(クリックで開く)
web/
├── src/
│ ├── App.tsx
│ ├── main.tsx
│ └── components/
│ └── TodoItem.tsx
└── package.json
セットアップコマンド(クリックで開く)
# 初回のみ:web アプリのひな型を生成
pnpm create vite@latest web -- --template react-ts
cd web
pnpm install
web/src/components/TodoItem.tsx(クリックで開く)
type Props = {
text: string;
done: boolean;
};
export function TodoItem({ text, done }: Props) {
return (
<li style={{ textDecoration: done ? 'line-through' : 'none' }}>
{text}
</li>
);
}
web/src/App.tsx(クリックで開く)
import { TodoItem } from './components/TodoItem';
const todos = [
{ id: 1, text: 'React のコンポーネント基本を学ぶ', done: true },
{ id: 2, text: 'Props でデータを流す', done: true },
{ id: 3, text: 'リストレンダリングを書く', done: false },
];
export default function App() {
return (
<main>
<h1>Todo</h1>
<ul>
{todos.map((t) => (
<TodoItem key={t.id} text={t.text} done={t.done} />
))}
</ul>
</main>
);
}
実行
pnpm dev
成功条件
- http://localhost:5173 を開くと、3 件の Todo が一覧表示されていること
-
done: trueの 2 件に取り消し線が付いていること - ブラウザの DevTools Console にエラーが出ていないこと
-
App.tsxの todos 配列に 4 件目を追加すると、画面にも即座に反映されること
次の章(b12)で 入力フォーム と チェックボックスでの done 切り替え を加える。今は props のみで状態はまだない。
参考リソース
- React公式ドキュメント(https://ja.react.dev/)— 日本語対応、インタラクティブなチュートリアル付き
- React Router v7(https://reactrouter.com/)
- 『Reactハンズオンラーニング』Leigh Halliday著(O’Reilly)
確認クイズ
Q1. JSXで と書いた場合、どうなるか。
正解: 警告が出るか意図通りに動かない。className を使う必要がある
解説: JSXはJavaScriptの構文拡張であり、class はJavaScriptの予約語。そのため属性名は className に変更されている。for も同様に htmlFor を使う。
Q2. Reactのリストレンダリングで key プロパティが必須な理由はどれか。 A. スタイルを適用するため B. ReactがDOM差分更新を正確に行うため C. Propsを渡すため D. TypeScriptの型チェックのため
正解: B. ReactがDOM差分更新を正確に行うため
解説: key はリストの各要素を一意に識別するためのもの。Reactは再レンダリング時に key を使ってどの要素が追加・変更・削除されたかを判断し、効率的なDOM更新を行う。key がないと誤ったコンポーネントが再利用される可能性がある。
Q3. Propsに ? を付けた場合(例:email?: string)、それはどういう意味か。
正解: そのPropsは省略可能(optional)であり、渡さなくても型エラーにならない
解説: TypeScriptのインターフェース定義でプロパティ名の後に ? を付けると undefined を許容する省略可能フィールドになる。コンポーネント内では email が undefined の可能性があるため、使用時には email && ... や email ?? "" などのguardが必要。
Q4. 「UIは状態の関数である(UI = f(state))」という考え方が意味することはどれか。 A. 状態を直接DOMに書き込む B. 状態が変わると自動的にUIが更新される C. UIからしか状態を変更できない D. 状態は1つしか持てない
正解: B. 状態が変わると自動的にUIが更新される
解説: Reactでは開発者が「状態をどう変えるか」だけを記述し、その状態に対応するUIはReactが自動的に計算・更新する。DOMを手動で操作する必要がないのがReactの核心的な設計思想。
Q5. コンポーネントのProps(プロパティ)の特徴として正しいものはどれか。 A. コンポーネント内部から変更できる B. 読み取り専用で、コンポーネントは変更しない C. 親子間でのみ共有できる双方向データ D. 型定義なしで使う必要がある
正解: B. 読み取り専用で、コンポーネントは変更しない
解説: PropsはコンポーネントへのインプットでありImmutable(不変)な値として扱う。純粋関数の考え方と同じく、同じPropsを渡せば同じ出力(UI)を返すことが保証される設計が望ましい。
Q6. web/src/pages/ と web/src/components/ の違いは何か。
正解: pages/ はURLに対応したページコンポーネント、components/ は複数のページやコンポーネントから再利用される部品コンポーネント
解説: pages/Home.tsx は / というURLに対応し、components/PostCard.tsx は複数のページで使い回す。この分類により、ページ固有のロジックと汎用UIパーツを明確に区別できる。
Q7. コンポーネント分割の目安として正しくないものはどれか。 A. 100行を超えたら分割を検討する B. 同じUIパターンが2箇所以上に現れたら共通化する C. ファイル数を減らすために1つの大きなコンポーネントにまとめる D. 「〇〇カード」「〇〇リスト」などと役割で命名できるか確認する
正解: C. ファイル数を減らすために1つの大きなコンポーネントにまとめる
解説: 単一責任原則に基づき、1つのコンポーネントは1つの役割を持つべき。ファイル数を減らすためにまとめると再利用性・テスト容易性・可読性が損なわれる。
Q8. JSXで複数の要素を返したいとき、余分なHTMLタグを追加せずにまとめる方法はどれか。 A. ... で必ず包む B. <>... フラグメントを使う C. 配列として返す(戻り値を [] に変える) D. コンポーネントを分割する
正解: B. <>...</> フラグメントを使う
解説: React.Fragment(省略記法 <>...</>)を使うと、実際のDOM要素を追加せずに複数の要素を1つのルート要素としてまとめられる。不要な <div> の追加を避けてHTMLをクリーンに保てる。
Q9. Propsのドリリングが問題になり始める目安は何階層を超えたときか。
正解: 3階層
解説: Props を3階層以上にわたって渡し続けると、中間コンポーネントが実際には使わないデータを「通過させるだけ」になりコードの見通しが悪化する。このタイミングで Context APIや状態管理ライブラリの導入を検討する(b12参照)。
Q10. React Router v7 で共通レイアウト(ヘッダーなど)を全ページに適用するために使うコンポーネント・概念はどれか。 A. index.tsx の直接描画 B. App.tsx に を使ったレイアウトパターン C. 各ページコンポーネントにHeaderを個別にインポートする D. router.tsx にスタイルを直接書く
正解: B. App.tsx に <Outlet /> を使ったレイアウトパターン
解説: React Router v7 では App.tsx を共通レイアウトコンポーネントとして定義し、<Outlet /> が子ルートのコンポーネントを描画するプレースホルダーになる。これにより Header などを一箇所に記述するだけで全ページに適用できる。
生きているコード
本ドキュメントで扱ったパターンの完全な動作コードは、メンター側リポジトリの参照ブランチで確認できます。
- 対応 Week: W2 / W9 / W10
- 参照ブランチ:
- W2:
reference/week-2 - W9:
reference/week-9 - W10:
reference/week-10 - 対応 checklist 項目: M2, M3
ブランチの作り方・見方は b00-curriculum-map を参照してください。
- 1. 📄Web とは何か
- 2. 📄URL を打ってから画面が表示されるまで
- 3. 📄ネットワーク基礎(TCP/IP・DNS・HTTPS)
- 4. 📄【発展】物理層から通信が成立するまで(電力・Ethernet・Wi-Fi・Bluetooth)
- 5. 📄WSL2・Docker セットアップ詳細(Windows 向け)
- 6. 📄環境構築の段階的導入(macOS / Windows)
- 7. 📄カリキュラム全体マップ(Week × 教材 × 参照ブランチ × 要求チェックリスト)
- 8. 📄このカリキュラムの使い方(SQL・Python・Dify経験者向け)
- 9. 📄シェル・ターミナル基礎
- 10. 📄Windows で完全にゼロから始める開発環境構築(Week 1)
- 11. 📄Git基礎
- 12. 📄GitHubワークフロー
- 13. 📄パッケージ管理(pnpm workspace)
- 1. 📄Reactコンポーネント設計の基礎
- 2. 📄状態管理の概念
- 3. 📄バックエンドAPI設計(Hono)
- 4. 📄ルーティング(React Router v7)
- 5. 📄Hono のエラーハンドリング(Month 4 Week 13)
- 6. 📄データベース設計・SQL→Drizzle ORM対応
- 7. 📄マイグレーションの考え方
- 1. 📄AWSインフラ基礎
- 2. 📄AWS Budget Alert の設定(Month 5 Week 17)
- 3. 📄環境変数管理
- 4. 📄Bastion EC2 と SSH ProxyJump(Month 5 Week 18)
- 5. 📄CI/CD基礎
- 6. 📄ECR への Docker イメージ push と App EC2 デプロイ(Month 5 Week 19)
- 7. 📄テスト設計の基本
- 8. 📄CloudFront + S3 + ALB で公開する(Month 5 Week 20)
- 9. 📄CLAUDE.md・プロジェクト設定
- 10. 📄PR レビュー 5 観点ルーブリック(全 Week 共通)
- 11. 📄タスク分解・仕様の書き方
- 12. 📄Playwright で E2E テスト(Month 6 Week 22)
- 13. 📄生成コードのレビュー・デバッグの勘所
- 14. 📄Trivy で脆弱性スキャン(Month 6 Week 23)
- 15. 📄CloudWatch Logs の読み方と運用(Month 6 Week 23)
- 16. 📄PDF ポートフォリオの自動生成(Month 6 Week 24)