コンポーネント

コンポーネントは React の最も重要な概念です。 UI を独立した再利用可能な部品に分割し、それぞれを個別に設計・実装できます。 この章では、関数コンポーネントの書き方、分割の考え方、ファイル構成のベストプラクティスを学びます。

コンポーネントとは

コンポーネントとは、UI の一部分を表す独立した部品です。 ボタン、ヘッダー、カード、フォームなど、画面上の任意の要素をコンポーネントとして定義できます。 React アプリケーションは、小さなコンポーネントを組み合わせて大きな画面を構築します。

身近な Web ページをコンポーネントの視点で見てみましょう。

Web ページのコンポーネント分割(概念図)
┌──────────────────────────────────────────┐
│  Header(ヘッダー)                         │
│  ┌──────┐  ┌─────────────────────────┐   │
│  │ Logo │  │ Navigation               │   │
│  └──────┘  └─────────────────────────┘   │
├──────────────────────────────────────────┤
│  Main(メインコンテンツ)                   │
│  ┌──────────┐  ┌──────────┐             │
│  │ Card     │  │ Card     │             │
│  │ ┌──────┐ │  │ ┌──────┐ │             │
│  │ │Image │ │  │ │Image │ │             │
│  │ └──────┘ │  │ └──────┘ │             │
│  │ Title    │  │ Title    │             │
│  │ Button   │  │ Button   │             │
│  └──────────┘  └──────────┘             │
├──────────────────────────────────────────┤
│  Footer(フッター)                         │
└──────────────────────────────────────────┘

この例では、HeaderLogoNavigationMainCardImageButtonFooter がそれぞれコンポーネントです。 Card コンポーネントは複数の場所で再利用されています。

関数コンポーネント

React のコンポーネントは JavaScript の関数 として定義します。 関数が JSX を返すと、それが画面に表示されます。 書き方には function 宣言とアロー関数の 2 種類があります。

function 宣言

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 で他のファイルから利用します。

src/components/Greeting.jsx(コンポーネントを定義)
// デフォルトエクスポート
function Greeting() {
  return <h2>こんにちは!</h2>;
}

export default Greeting;
src/App.jsx(コンポーネントを使用)
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 初心者が最もよく悩むポイントです。 以下のガイドラインを参考にしてください。

最初は粗い粒度で始めて、必要に応じて分割していくアプローチがおすすめです。 最初から細かく分けすぎると、ファイルが増えすぎてかえって見通しが悪くなります。

分割の実践例

1 つの大きなコンポーネントを、適切な粒度に分割してみましょう。

分割前: すべてが 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>&copy; 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>&copy; 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 の子

この階層構造における用語を整理しておきましょう。

用語 説明
親コンポーネント 他のコンポーネントをレンダリングするコンポーネント HeaderLogoNavigation の親
子コンポーネント 別のコンポーネントの中でレンダリングされるコンポーネント LogoHeader の子
兄弟コンポーネント 同じ親を持つコンポーネント同士 LogoNavigation は兄弟
ルートコンポーネント ツリーの最上位にあるコンポーネント App

React Developer Tools(ブラウザ拡張機能)を使うと、 コンポーネントツリーをリアルタイムで視覚的に確認できます。 デバッグにとても便利なので、Chrome や Firefox にインストールしておきましょう。

ファイル構成のベストプラクティス

命名規則

対象 命名規則
コンポーネント名 PascalCase(先頭大文字) UserProfileProductCard
コンポーネントファイル名 PascalCase.jsx UserProfile.jsxProductCard.jsx
ユーティリティ関数ファイル camelCase.js formatDate.jshelpers.js
CSS ファイル コンポーネント名に合わせる UserProfile.cssProductCard.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 の基本
// 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 について学びます。