コンポーネント設計
Reactアプリはコンポーネントの組み合わせで構成されます。再利用可能な部品として設計することで、保守性が高く効率的な開発ができます。
コンポーネント設計の考え方
コンポーネント分割の基準
1. 再利用性
複数の場所で使う部品はコンポーネント化
例: ボタン、カード、フォーム入力
2. 単一責任
1つのコンポーネントは1つの役割
例: Header、ArticleCard、LoginForm
3. 適切な粒度
大きすぎず、小さすぎず
例: Buttonは良い、ButtonTextは細かすぎ
4. テスト容易性
独立してテストできる単位
例: ArticleListはArticleCardを使う
フォルダ構成
Next.js推奨構成
src/
├── app/ # Next.js App Router
│ ├── layout.tsx # 共通レイアウト
│ ├── page.tsx # トップページ
│ ├── login/
│ │ └── page.tsx
│ ├── register/
│ │ └── page.tsx
│ └── posts/
│ ├── page.tsx # 記事一覧
│ ├── new/
│ │ └── page.tsx # 記事作成
│ └── [id]/
│ ├── page.tsx # 記事詳細
│ └── edit/
│ └── page.tsx
├── components/ # 再利用コンポーネント
│ ├── ui/ # 汎用UI
│ │ ├── Button.tsx
│ │ ├── Input.tsx
│ │ └── Card.tsx
│ ├── layout/ # レイアウト系
│ │ ├── Header.tsx
│ │ └── Footer.tsx
│ └── features/ # 機能別
│ ├── auth/
│ │ ├── LoginForm.tsx
│ │ └── RegisterForm.tsx
│ └── posts/
│ ├── ArticleCard.tsx
│ ├── ArticleList.tsx
│ └── ArticleForm.tsx
├── hooks/ # カスタムフック
│ ├── useAuth.ts
│ └── usePosts.ts
├── lib/ # ユーティリティ
│ └── api.ts
└── types/ # 型定義
└── index.ts
ポイント:components/の下は「用途別」に分けることで、どこに何があるか分かりやすくなります。
ブログアプリのコンポーネント設計
汎用UIコンポーネント
Button
Props: variant, size, onClick, disabled, children
Primary
Secondary
Input
Props: type, placeholder, value, onChange, error
Card
Props: children, className
カードコンテンツ
機能コンポーネント
| コンポーネント | 役割 | 主なProps |
|---|---|---|
| Header | ナビゲーション、ログイン状態表示 | user, onLogout |
| LoginForm | ログインフォーム | onSubmit, isLoading |
| RegisterForm | 新規登録フォーム | onSubmit, isLoading |
| ArticleCard | 記事のカード表示 | article |
| ArticleList | 記事一覧表示 | articles |
| ArticleForm | 記事作成・編集フォーム | article?, onSubmit, isLoading |
コード例
Button.tsx
type ButtonProps = {
variant?: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
onClick?: () => void;
children: React.ReactNode;
};
export function Button({
variant = 'primary',
size = 'md',
disabled = false,
onClick,
children
}: ButtonProps) {
const baseStyle = 'rounded font-bold transition';
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
danger: 'bg-red-600 text-white hover:bg-red-700',
};
const sizes = {
sm: 'px-3 py-1 text-sm',
md: 'px-4 py-2',
lg: 'px-6 py-3 text-lg',
};
return (
<button
className={`${baseStyle} ${variants[variant]} ${sizes[size]}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
}
ArticleCard.tsx
import Link from 'next/link';
import { Article } from '@/types';
type ArticleCardProps = {
article: Article;
};
export function ArticleCard({ article }: ArticleCardProps) {
return (
<Link href={`/posts/${article.id}`}>
<div className="bg-white rounded-lg shadow p-6 hover:shadow-md transition">
<h2 className="text-xl font-bold mb-2">{article.title}</h2>
<p className="text-gray-600 text-sm mb-4 line-clamp-2">
{article.content}
</p>
<div className="flex justify-between text-xs text-gray-400">
<span>{article.author.name}</span>
<span>{new Date(article.createdAt).toLocaleDateString()}</span>
</div>
</div>
</Link>
);
}
AIにコンポーネント設計を相談
AIへの指示例
ブログアプリのコンポーネント設計を手伝ってください。 【アプリ概要】 - 認証付きブログアプリ - 記事のCRUD、ユーザー登録・ログイン 【設計してほしいコンポーネント】 1. 汎用UIコンポーネント(Button, Input, Card, Modal) 2. 機能コンポーネント(Header, LoginForm, ArticleCard等) 【要望】 - TypeScriptで型定義も含めて - Tailwind CSSを使用 - 再利用しやすい設計で
まとめ
- ✓ コンポーネントは「再利用性」「単一責任」を意識して設計
- ✓ フォルダ構成は「用途別」に整理する
- ✓ 汎用UI(Button, Input等)と機能コンポーネントを分ける
- ✓ Propsの型定義で使い方を明確にする