コンポーネント
コンポーネントは React の最も重要な概念です。 UI を独立した再利用可能な部品に分割し、それぞれを個別に設計・実装できます。 この章では、関数コンポーネントの書き方、分割の考え方、ファイル構成のベストプラクティスを学びます。
コンポーネントとは
コンポーネントとは、UI の一部分を表す独立した部品です。 ボタン、ヘッダー、カード、フォームなど、画面上の任意の要素をコンポーネントとして定義できます。 React アプリケーションは、小さなコンポーネントを組み合わせて大きな画面を構築します。
身近な Web ページをコンポーネントの視点で見てみましょう。
┌──────────────────────────────────────────┐
│ Header(ヘッダー) │
│ ┌──────┐ ┌─────────────────────────┐ │
│ │ Logo │ │ Navigation │ │
│ └──────┘ └─────────────────────────┘ │
├──────────────────────────────────────────┤
│ Main(メインコンテンツ) │
│ ┌──────────┐ ┌──────────┐ │
│ │ Card │ │ Card │ │
│ │ ┌──────┐ │ │ ┌──────┐ │ │
│ │ │Image │ │ │ │Image │ │ │
│ │ └──────┘ │ │ └──────┘ │ │
│ │ Title │ │ Title │ │
│ │ Button │ │ Button │ │
│ └──────────┘ └──────────┘ │
├──────────────────────────────────────────┤
│ Footer(フッター) │
└──────────────────────────────────────────┘
この例では、Header、Logo、Navigation、Main、
Card、Image、Button、Footer がそれぞれコンポーネントです。
Card コンポーネントは複数の場所で再利用されています。
関数コンポーネント
React のコンポーネントは JavaScript の関数 として定義します。
関数が JSX を返すと、それが画面に表示されます。
書き方には function 宣言とアロー関数の 2 種類があります。
function 宣言
function Welcome() {
return <h1>ようこそ!</h1>;
}
アロー関数
const Welcome = () => {
return <h1>ようこそ!</h1>;
};
// JSX が 1 行の場合、return と波括弧を省略できる
const Welcome = () => <h1>ようこそ!</h1>;
function 宣言とアロー関数のどちらを使うかはプロジェクトやチームの方針によります。
機能的な違いはほぼありませんが、以下の特徴があります。
function宣言: ホイスティング(宣言前に使用可能)される。名前がデバッグツールに表示されやすい- アロー関数:
constで定義するため、誤って再代入される心配がない。簡潔に書ける
コンポーネント名は必ず大文字で始めてください。
React は大文字で始まる要素をコンポーネントとして、小文字で始まる要素を HTML 要素として扱います。
<button> は HTML の button 要素、<Button> は React の Button コンポーネントです。
コンポーネントの使い方
import と export
React では、各コンポーネントを個別のファイルに定義し、
export / import で他のファイルから利用します。
// デフォルトエクスポート
function Greeting() {
return <h2>こんにちは!</h2>;
}
export default Greeting;
import Greeting from './components/Greeting';
function App() {
return (
<div>
<Greeting />
</div>
);
}
export default App;
export には 2 種類あります。
| 種類 | export の書き方 | import の書き方 | 特徴 |
|---|---|---|---|
| デフォルト | export default Component |
import Component from './file' |
1 ファイルに 1 つ。import 時に名前を変えられる |
| 名前付き | export function Component() |
import { Component } from './file' |
1 ファイルに複数可。import 時に波括弧が必要 |
// utils/helpers.jsx - 複数の関数やコンポーネントをエクスポート
export function formatDate(date) {
return date.toLocaleDateString('ja-JP');
}
export function formatCurrency(amount) {
return `¥${amount.toLocaleString()}`;
}
// 使う側
import { formatDate, formatCurrency } from './utils/helpers';
コンポーネントのレンダリング
コンポーネントは JSX の中で HTML タグと同じように使います。
自己閉じタグ <Component /> または開閉タグ <Component>...</Component> の
どちらの形式でも使えます。
import Header from './components/Header';
import Footer from './components/Footer';
import Card from './components/Card';
function App() {
return (
<>
<Header />
<main>
{/* 同じコンポーネントを複数回使える */}
<Card />
<Card />
<Card />
</main>
<Footer />
</>
);
}
コンポーネントの分割
分割するタイミング
「どの粒度でコンポーネントを分割すべきか」は、React 初心者が最もよく悩むポイントです。 以下のガイドラインを参考にしてください。
- 再利用する場合 — 同じ UI パーツが複数箇所で使われるなら、コンポーネントにする
- 複雑すぎる場合 — 1 つのコンポーネントが 100 行を超えたら、分割を検討する
- 責務が異なる場合 — ヘッダーとメインコンテンツは別の役割なので、別コンポーネントにする
- 独立してテストしたい場合 — テストしたい単位でコンポーネントを分ける
- 状態の管理範囲 — ある state が一部の UI にしか影響しないなら、その部分をコンポーネントに切り出す
最初は粗い粒度で始めて、必要に応じて分割していくアプローチがおすすめです。 最初から細かく分けすぎると、ファイルが増えすぎてかえって見通しが悪くなります。
分割の実践例
1 つの大きなコンポーネントを、適切な粒度に分割してみましょう。
function ProductPage() {
return (
<div>
<header>
<img src="/logo.svg" alt="Logo" />
<nav>
<a href="/">ホーム</a>
<a href="/products">商品一覧</a>
</nav>
</header>
<main>
<img src="/product.jpg" alt="商品画像" />
<h1>商品名</h1>
<p>価格: ¥1,000</p>
<p>商品の説明文...</p>
<button>カートに追加</button>
</main>
<footer>
<p>© 2026 My Shop</p>
</footer>
</div>
);
}
// src/components/Header.jsx
function Header() {
return (
<header>
<img src="/logo.svg" alt="Logo" />
<nav>
<a href="/">ホーム</a>
<a href="/products">商品一覧</a>
</nav>
</header>
);
}
export default Header;
// src/components/ProductDetail.jsx
function ProductDetail({ product }) {
return (
<main>
<img src={product.image} alt={product.name} />
<h1>{product.name}</h1>
<p>価格: ¥{product.price.toLocaleString()}</p>
<p>{product.description}</p>
<button>カートに追加</button>
</main>
);
}
export default ProductDetail;
// src/components/Footer.jsx
function Footer() {
return (
<footer>
<p>© 2026 My Shop</p>
</footer>
);
}
export default Footer;
// src/pages/ProductPage.jsx - 各コンポーネントを組み合わせる
import Header from '../components/Header';
import ProductDetail from '../components/ProductDetail';
import Footer from '../components/Footer';
function ProductPage() {
const product = {
name: 'サンプル商品',
price: 1000,
image: '/product.jpg',
description: '商品の説明文...',
};
return (
<>
<Header />
<ProductDetail product={product} />
<Footer />
</>
);
}
export default ProductPage;
コンポーネントの階層構造
React アプリケーションのコンポーネントは、ツリー状の 親子関係 を持ちます。
最上位の App コンポーネントから始まり、その中に子コンポーネントがネストされていきます。
この構造を コンポーネントツリー と呼びます。
App ← ルートコンポーネント
├── Header ← App の子
│ ├── Logo ← Header の子
│ └── Navigation ← Header の子
│ ├── NavLink ← Navigation の子
│ ├── NavLink
│ └── NavLink
├── Main ← App の子
│ ├── SearchBar ← Main の子
│ └── ProductList ← Main の子
│ ├── ProductCard ← ProductList の子
│ ├── ProductCard
│ └── ProductCard
└── Footer ← App の子
この階層構造における用語を整理しておきましょう。
| 用語 | 説明 | 例 |
|---|---|---|
| 親コンポーネント | 他のコンポーネントをレンダリングするコンポーネント | Header は Logo と Navigation の親 |
| 子コンポーネント | 別のコンポーネントの中でレンダリングされるコンポーネント | Logo は Header の子 |
| 兄弟コンポーネント | 同じ親を持つコンポーネント同士 | Logo と Navigation は兄弟 |
| ルートコンポーネント | ツリーの最上位にあるコンポーネント | App |
React Developer Tools(ブラウザ拡張機能)を使うと、 コンポーネントツリーをリアルタイムで視覚的に確認できます。 デバッグにとても便利なので、Chrome や Firefox にインストールしておきましょう。
ファイル構成のベストプラクティス
命名規則
| 対象 | 命名規則 | 例 |
|---|---|---|
| コンポーネント名 | PascalCase(先頭大文字) | UserProfile、ProductCard |
| コンポーネントファイル名 | PascalCase.jsx | UserProfile.jsx、ProductCard.jsx |
| ユーティリティ関数ファイル | camelCase.js | formatDate.js、helpers.js |
| CSS ファイル | コンポーネント名に合わせる | UserProfile.css、ProductCard.module.css |
| フォルダ名 | camelCase または kebab-case | components/、utils/、hooks/ |
フォルダ構成パターン
パターン 1: 種類別(小〜中規模向け)
src/
├── components/ # 再利用可能なコンポーネント
│ ├── Header.jsx
│ ├── Header.css
│ ├── Footer.jsx
│ ├── Button.jsx
│ └── Card.jsx
├── pages/ # ページ単位のコンポーネント
│ ├── HomePage.jsx
│ ├── ProductPage.jsx
│ └── AboutPage.jsx
├── hooks/ # カスタムフック
│ └── useLocalStorage.js
├── utils/ # ユーティリティ関数
│ └── formatDate.js
├── App.jsx
├── App.css
├── main.jsx
└── index.css
パターン 2: 機能別(中〜大規模向け)
src/
├── features/ # 機能ごとにまとめる
│ ├── auth/ # 認証機能
│ │ ├── LoginForm.jsx
│ │ ├── LoginForm.css
│ │ └── useAuth.js
│ ├── products/ # 商品機能
│ │ ├── ProductList.jsx
│ │ ├── ProductCard.jsx
│ │ ├── ProductDetail.jsx
│ │ └── useProducts.js
│ └── cart/ # カート機能
│ ├── Cart.jsx
│ ├── CartItem.jsx
│ └── useCart.js
├── components/ # 共通コンポーネント
│ ├── Button.jsx
│ ├── Modal.jsx
│ └── Loading.jsx
├── App.jsx
└── main.jsx
最初は「パターン 1: 種類別」で始めて、プロジェクトの成長に合わせて 「パターン 2: 機能別」に移行するのがおすすめです。 大切なのはチーム内で一貫性を保つことです。
children
React コンポーネントには children という特別な props があります。
コンポーネントの開きタグと閉じタグの間に書いた内容が children として渡されます。
// children を受け取るコンポーネント
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
// 使用例: 開きタグと閉じタグの間の内容が children になる
<Card>
<h2>カードのタイトル</h2>
<p>カードの本文です。</p>
</Card>
// 別の内容でも同じ Card コンポーネントを再利用できる
<Card>
<img src="/photo.jpg" alt="写真" />
<p>写真の説明</p>
</Card>
コンポジションパターン
children を活用すると、コンポジション(合成) というパターンで
柔軟なコンポーネントを作れます。外側のレイアウトやスタイルを共通化しつつ、
中身を自由に差し替えられます。
// 共通レイアウトを提供するコンポーネント
function PageLayout({ children }) {
return (
<div className="page-layout">
<Header />
<main className="main-content">
{children}
</main>
<Footer />
</div>
);
}
// ページごとに中身だけ変える
function HomePage() {
return (
<PageLayout>
<h1>ホームページ</h1>
<p>ようこそ!</p>
</PageLayout>
);
}
function AboutPage() {
return (
<PageLayout>
<h1>このサイトについて</h1>
<p>サイトの説明...</p>
</PageLayout>
);
}
さらに、複数の children の「スロット」が必要な場合は、
通常の props として JSX を渡すこともできます。
// sidebar と content を別々に受け取る
function SplitLayout({ sidebar, children }) {
return (
<div className="split-layout">
<aside className="sidebar">
{sidebar}
</aside>
<main className="content">
{children}
</main>
</div>
);
}
// 使用例
<SplitLayout sidebar={<NavigationMenu />}>
<h1>メインコンテンツ</h1>
<p>ここが本文です。</p>
</SplitLayout>
コンポジションパターンは React で最も重要なデザインパターンの一つです。 コンポーネントの「継承」ではなく「合成」で UI を構築するのが React の思想です。 React の公式ドキュメントでも、継承よりもコンポジションの使用を推奨しています。
以上でコンポーネントの基本を学びました。 次の章では、コンポーネント間でデータを受け渡す Props と、 コンポーネント内部の状態を管理する State について学びます。