カテゴリ: React 更新日: 2026/02/28

ReactのPresentational ComponentとContainer Componentを完全理解!役割と使い分けを徹底解説

Presentational ComponentとContainer Componentの違いと役割
Presentational ComponentとContainer Componentの違いと役割

先生と生徒の会話形式で理解しよう

生徒

「ReactでPresentational ComponentとContainer Componentという言葉を聞いたんですけど、何が違うんですか?」

先生

「これは、コンポーネントの役割を分けて整理するための考え方です。それぞれに明確な役割があるんですよ。」

生徒

「役割を分けるって、具体的にどういうメリットがあるんですか?」

先生

「プログラムが整理されて、修正やテストがとても楽になります。それでは、詳しく見ていきましょう!」

1. Presentational ComponentとContainer Componentとは?

1. Presentational ComponentとContainer Componentとは?
1. Presentational ComponentとContainer Componentとは?

Reactでアプリケーションを作るとき、コンポーネントを二つの種類に分けて考える方法があります。それが、Presentational Component(プレゼンテーショナルコンポーネント)とContainer Component(コンテナコンポーネント)です。この考え方は、レストランの役割分担に例えることができます。

レストランでは、料理を作るシェフと、料理を運んで提供するホールスタッフがいますよね。シェフは調理に専念し、ホールスタッフは接客に専念します。Reactのコンポーネントも同じように、見た目を担当するコンポーネントと、データや処理を担当するコンポーネントに分けることで、それぞれの役割が明確になるのです。

Presentational Componentは、画面の見た目だけを担当するコンポーネントです。一方、Container Componentは、データの取得や状態の管理など、裏側の処理を担当します。この役割分担により、プログラムが読みやすく、管理しやすくなります。

2. Presentational Componentの特徴と役割

2. Presentational Componentの特徴と役割
2. Presentational Componentの特徴と役割

Presentational Componentは、見た目だけを担当するコンポーネントです。このコンポーネントは、データをどこから取得するのか、どう処理するのかは知りません。ただ、受け取ったデータをどう表示するかだけに集中します。

例えば、テレビのアナウンサーを想像してください。アナウンサーは、原稿を渡されたらそれを読み上げるだけで、原稿の内容を自分で考えたり調査したりはしません。Presentational Componentも同じで、親コンポーネントから渡されたpropsを受け取って、それを画面に表示するだけなのです。

Presentational Componentの特徴は、useStateuseEffectなどの状態管理をほとんど使わないことです。すべてのデータはpropsとして受け取り、表示に専念します。これにより、同じコンポーネントを様々な場所で再利用しやすくなります。

3. Presentational Componentの実装例

3. Presentational Componentの実装例
3. Presentational Componentの実装例

それでは、実際にPresentational Componentを作ってみましょう。ユーザー情報を表示するシンプルなコンポーネントです。


import React from "react";

function UserProfile(props) {
  return (
    <div style={{
      border: "2px solid #007bff",
      padding: "20px",
      borderRadius: "8px",
      maxWidth: "300px"
    }}>
      <h3>{props.name}</h3>
      <p>年齢: {props.age}歳</p>
      <p>職業: {props.job}</p>
      <p>メール: {props.email}</p>
    </div>
  );
}

export default UserProfile;
このコンポーネントは、名前、年齢、職業、メールアドレスを受け取って表示するだけです。データがどこから来たのか、どう取得されたのかは全く関知しません。

このコンポーネントには、データの取得や状態の管理は一切含まれていません。すべてpropsから受け取った値を表示しているだけです。このように作ることで、テストが簡単になり、どんなデータでも表示できる汎用的なコンポーネントになります。

4. Container Componentの特徴と役割

4. Container Componentの特徴と役割
4. Container Componentの特徴と役割

Container Componentは、データの管理と処理を担当するコンポーネントです。このコンポーネントは、APIからデータを取得したり、状態を管理したり、計算処理を行ったりします。そして、処理したデータをPresentational Componentに渡す役割を持ちます。

先ほどのレストランの例で言えば、Container Componentはシェフの役割です。材料を仕入れて、下ごしらえをして、料理を作ります。そして、完成した料理をホールスタッフ(Presentational Component)に渡すのです。

Container Componentの特徴は、useStateuseEffectuseContextなどのReact Hooksを積極的に使うことです。また、外部のAPIと通信したり、複雑な計算処理を行ったりします。見た目については気にせず、データの準備と管理に専念します。

5. Container Componentの実装例

5. Container Componentの実装例
5. Container Componentの実装例

次に、Container Componentを作ってみましょう。ユーザー情報を管理して、先ほどのUserProfileコンポーネントにデータを渡す例です。


import React, { useState, useEffect } from "react";
import UserProfile from "./UserProfile";

function UserContainer() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // 実際のアプリでは、ここでAPIからデータを取得します
    setTimeout(() => {
      const userData = {
        name: "山田太郎",
        age: 28,
        job: "エンジニア",
        email: "yamada@example.com"
      };
      setUser(userData);
      setLoading(false);
    }, 1000);
  }, []);

  if (loading) {
    return <p>読み込み中...</p>;
  }

  return <UserProfile {...user} />;
}

export default UserContainer;
このコンポーネントは、データの取得と状態管理を行い、準備ができたらUserProfileコンポーネントに渡します。最初は「読み込み中...」と表示され、1秒後にユーザー情報が表示されます。

useEffectは、コンポーネントが画面に表示されたときに実行される処理を書く場所です。ここでは、データの取得を模擬しています。実際のアプリケーションでは、fetchaxiosなどを使ってサーバーからデータを取得します。

6. 二つのコンポーネントを組み合わせる具体例

6. 二つのコンポーネントを組み合わせる具体例
6. 二つのコンポーネントを組み合わせる具体例

Presentational ComponentとContainer Componentを組み合わせた、より実践的な例を見てみましょう。商品リストを表示するアプリケーションです。


import React from "react";

// Presentational Component - 商品カードの見た目だけを担当
function ProductCard(props) {
  return (
    <div style={{
      border: "1px solid #ddd",
      padding: "15px",
      margin: "10px",
      borderRadius: "5px"
    }}>
      <h4>{props.name}</h4>
      <p style={{ color: "#666" }}>{props.description}</p>
      <p style={{ fontSize: "20px", fontWeight: "bold", color: "#28a745" }}>
        ¥{props.price.toLocaleString()}
      </p>
      <button onClick={() => props.onAddToCart(props.id)} style={{
        backgroundColor: "#007bff",
        color: "white",
        padding: "8px 16px",
        border: "none",
        borderRadius: "4px",
        cursor: "pointer"
      }}>
        カートに追加
      </button>
    </div>
  );
}

export default ProductCard;
このPresentational Componentは、商品の情報を受け取って表示するだけです。カートに追加する処理は、親コンポーネントから渡された関数を呼び出すだけで、具体的な処理内容は知りません。

このように、見た目のコンポーネントは処理の詳細を知らなくても良いようにします。ボタンがクリックされたら、渡された関数を実行するだけです。これにより、同じ商品カードを様々な場所で使い回すことができます。

7. Container Componentでデータと処理を管理する

7. Container Componentでデータと処理を管理する
7. Container Componentでデータと処理を管理する

次に、商品リストを管理するContainer Componentを作ります。このコンポーネントが、データの準備とカート機能の実装を担当します。


import React, { useState } from "react";
import ProductCard from "./ProductCard";

function ProductListContainer() {
  const [products] = useState([
    { id: 1, name: "ノートパソコン", description: "高性能で軽量", price: 89800 },
    { id: 2, name: "ワイヤレスマウス", description: "快適な操作性", price: 2980 },
    { id: 3, name: "キーボード", description: "静音タイプ", price: 5800 }
  ]);

  const [cart, setCart] = useState([]);

  const handleAddToCart = (productId) => {
    const product = products.find(p => p.id === productId);
    setCart([...cart, product]);
    alert(`${product.name}をカートに追加しました!`);
  };

  return (
    <div style={{ padding: "20px" }}>
      <h1>商品一覧</h1>
      <p>カートの商品数: {cart.length}個</p>
      <div>
        {products.map(product => (
          <ProductCard
            key={product.id}
            {...product}
            onAddToCart={handleAddToCart}
          />
        ))}
      </div>
    </div>
  );
}

export default ProductListContainer;
商品一覧が表示され、各商品にカートに追加ボタンがあります。ボタンを押すと、カートの商品数が増えて、アラートが表示されます。すべてのロジックはContainer Componentで管理されています。

このContainer Componentは、商品データの管理、カートの状態管理、カートに追加する処理など、すべての複雑な処理を担当しています。ProductCardコンポーネントは、これらの処理を知らなくても、渡されたデータを表示するだけで機能します。

8. コンポーネント分離のメリット

8. コンポーネント分離のメリット
8. コンポーネント分離のメリット

Presentational ComponentとContainer Componentを分けることで、様々なメリットが得られます。まず、コードの見通しが良くなります。見た目の部分と処理の部分が分かれているので、どこを修正すれば良いかすぐに分かります。デザインを変えたいときはPresentational Componentだけを、処理を変えたいときはContainer Componentだけを修正すれば良いのです。

また、再利用性が高まります。Presentational Componentは、どんなデータでも表示できる汎用的な作りになっているため、様々な場面で使い回せます。例えば、ユーザープロフィールの表示コンポーネントは、管理画面でも、マイページでも、検索結果でも使えるのです。

テストもしやすくなります。Presentational Componentは、特定のpropsを渡せば決まった表示になるため、自動テストが書きやすいです。Container Componentも、データ取得や状態管理のロジックだけに集中してテストできます。さらに、チーム開発の際には、デザイナーがPresentational Componentを、エンジニアがContainer Componentを担当するという分業もしやすくなります。

9. 現代のReactにおける考え方の変化

9. 現代のReactにおける考え方の変化
9. 現代のReactにおける考え方の変化

Presentational ComponentとContainer Componentという考え方は、React Hooksが登場する前によく使われていました。当時は、クラスコンポーネントという書き方が主流で、状態を持つコンポーネントと持たないコンポーネントを明確に分ける必要があったのです。

しかし、React Hooksの登場により、どのコンポーネントでも簡単に状態を持てるようになりました。そのため、厳密にPresentational ComponentとContainer Componentを分ける必要性は減ってきています。現代のReact開発では、もっと柔軟に考えることが多くなりました。

ただし、この考え方の本質である「見た目と処理を分離する」という原則は、今でも非常に重要です。カスタムフックという機能を使って処理をまとめたり、コンポーネントの責任を明確にしたりすることで、同じような効果が得られます。大切なのは、分類にこだわることではなく、コードを整理して読みやすくする意識を持つことです。

10. 実践的な使い分けのポイント

10. 実践的な使い分けのポイント
10. 実践的な使い分けのポイント

実際の開発では、すべてのコンポーネントを厳密にPresentational ComponentとContainer Componentに分ける必要はありません。小規模なアプリケーションでは、むしろ一つのコンポーネントにまとめた方がシンプルで分かりやすいこともあります。

コンポーネントを分けるべきタイミングは、以下のような場合です。まず、同じ見た目を複数の場所で使いたいとき。次に、コンポーネントが複雑になりすぎて読みにくくなったとき。そして、デザインと処理を別々の人が担当するとき。これらの状況では、コンポーネントを分離することで開発効率が上がります。

また、APIからデータを取得する処理や、複雑な状態管理が必要な場合は、Container Componentとして分離すると良いでしょう。一方、ボタンやカード、リストアイテムなど、純粋に表示だけを担当する小さなコンポーネントは、Presentational Componentとして作ります。重要なのは、各コンポーネントの責任を明確にして、修正しやすく、テストしやすい構造を保つことです。経験を積むにつれて、適切な分割のバランス感覚が身についていきます。

カテゴリの一覧へ
新着記事
New1
React
ReactのPresentational ComponentとContainer Componentを完全理解!役割と使い分けを徹底解説
New2
React
ReactのuseEffectでローディング状態を管理する方法を解説!初心者でもできる非同期処理の基本
New3
React
ReactでPropsの型チェックをする方法(PropTypesとTypeScript)をやさしく解説
New4
React
Reactコンポーネントのアンチパターンを徹底解説!初心者でも避けるべき使ってはいけない書き方
人気記事
No.1
Java&Spring記事人気No1
React
ReactとTypeScriptの環境構築をやさしく解説!Viteとtsconfigの設定も丁寧に紹介
No.2
Java&Spring記事人気No2
React
ReactでonChangeイベントを使ってフォーム入力値を管理する方法を初心者向けに解説
No.3
Java&Spring記事人気No3
React
create-react-appでReactプロジェクトを作成する手順を初心者向けに完全解説!
No.4
Java&Spring記事人気No4
React
Reactのカスタムフックの作り方を完全ガイド!再利用可能なロジックを切り出す仕組み
No.5
Java&Spring記事人気No5
React
Reactのイベントハンドリングのアンチパターンまとめ!初心者でもわかる注意点
No.6
Java&Spring記事人気No6
React
Reactでファイルアップロードを実装する方法を解説!Fetch APIで画像やPDFを送る仕組みを初心者向けに紹介
No.7
Java&Spring記事人気No7
React
ViteでReact開発環境を構築する手順を完全ガイド!初心者でもできるReactの環境構築
No.8
Java&Spring記事人気No8
React
useEffectでクリーンアップ関数を使う方法をやさしく解説