presentational-container-pattern について
React 開発者向けに、Container/Presentational パターンでデータや状態の処理と UI 描画を分ける方法を解説します。再利用性やテストの利点に加え、小さなコンポーネントでの過剰な分割や、代替となるカスタムフックも扱います。
主な機能
- 01データ処理と UI 描画の分離
- 02props によるデータ受け渡し
- 03再利用・テストしやすい表示コンポーネント
- 04採用・回避する場面の説明
- 05代替となるカスタムフックの紹介
活用シーン
- 01データ取得と UI 描画の分離
- 02再利用可能な表示コンポーネントの構築
- 03小さなコンポーネントの分割要否の判断
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ patternsdev/ skills --skill presentational-container-patternインストール後、Claude Code で/presentational-container-patternを入力して読み込まれたか確認します。
詳細
Container/Presentational パターン
React では、Container/Presentational パターンを使うことで、関心事を分離できます。このパターンでは、ビューとアプリケーションロジックを分離できます。
使用する場合
- データ取得のロジックと UI のレンダリングを明確に分離したい場合に使用します
- Presentational コンポーネントを再利用しやすく、テストしやすくするのに役立ちます
使用しない場合
- 2 つのファイルに分けることで、明確なメリットがないままオーバーヘッドが増える小さなコンポーネント
- フックですでにデータロジックをカプセル化しており、コンテナコンポーネントを別に作る必要がない場合
- どちらのレイヤーにも再利用の可能性がない、一度限りのビューである場合
手順
- Container コンポーネントはデータ取得と状態管理を行い、Presentational コンポーネントはレンダリングを行います
- 現代の React では、同じ関心事の分離には Container コンポーネントよりカスタムフックを優先します
- Presentational コンポーネントは、props を通じてデータを受け取る純粋関数として保ちます
- アーキテクチャが本当にシンプルになる場合にこのパターンを使用し、小さなコンポーネントでは避けます
詳細
犬の画像を 6 枚取得して、画面に表示するアプリケーションを作成するとします。
理想的には、このプロセスを次の 2 つの部分に分けて、関心事を分離します。
- Presentational コンポーネント:データをユーザーに_どのように_表示するかを担うコンポーネントです。この例では、_犬の画像のリストをレンダリングすること_です。
- Container コンポーネント:ユーザーに_何を_表示するかを担うコンポーネントです。この例では、_犬の画像を取得すること_です。
犬の画像の取得はアプリケーションロジックを扱い、画像の表示はビューのみを扱います。
Presentational コンポーネント
Presentational コンポーネントは props を通じてデータを受け取ります。その主な役割は、スタイルを含め、受け取ったデータを変更することなく、望ましい方法で表示することです。
犬の画像を表示する例を見てみましょう。犬の画像をレンダリングするときは、API から取得した犬の画像を 1 枚ずつ処理し、それらをレンダリングするだけです。そのために、props を通じてデータを受け取り、受け取ったデータをレンダリングする関数コンポーネントを作成できます。
DogImages コンポーネントは Presentational コンポーネントです。Presentational コンポーネントは_通常_ステートレスであり、UI のために状態が必要な場合を除き、独自の React state を持ちません。受け取ったデータは、Presentational コンポーネント自身によって変更されません。
Presentational コンポーネントは、Container コンポーネントからデータを受け取ります。
Container コンポーネント
Container コンポーネントの主な役割は、自身が_内包する_ Presentational コンポーネントにデータを渡すことです。Container コンポーネントは通常、データを扱う Presentational コンポーネント以外はレンダリングしません。自身では何もレンダリングしないため、通常はスタイルも持ちません。
この例では、犬の画像を Presentational コンポーネントの DogsImages に渡します。その前に、外部 API から画像を取得する必要があります。このデータを取得し、画面に表示するために Presentational コンポーネントの DogImages に渡すContainer コンポーネントを作成する必要があります。
これら 2 つのコンポーネントを組み合わせることで、アプリケーションロジックの処理とビューを分離できます。
フック
多くの場合、Container/Presentational パターンは React フックで置き換えられます。フックの導入によって、状態を提供するコンテナコンポーネントを用意しなくても、開発者が簡単に状態を追加できるようになりました。
DogImagesContainer コンポーネントにデータ取得ロジックを置く代わりに、画像を取得して犬の配列を返すカスタムフックを作成できます。
export default function useDogImages() {
const [dogs, setDogs] = useState([]);
useEffect(() => {
fetch("https://dog.ceo/api/breed/labrador/images/random/6")
.then((res) => res.json())
.then(({ message }) => setDogs(message));
}, []);
return dogs;
}
このフックを使えば、データを取得してプレゼンテーショナルな DogImages コンポーネントに渡すための、ラッパーとなる DogImagesContainer コンテナコンポーネントは不要になります。代わりに、このフックをプレゼンテーショナルな DogImages コンポーネント内で直接使えます。
useDogImages フックを使うことで、アプリケーションのロジックとビューは引き続き分離されています。DogImages コンポーネント内でデータを変更せず、useDogImages フックが返すデータをそのまま使っています。
フックを使うと、Container/Presentational パターンと同じように、コンポーネント内でロジックとビューを簡単に分離できます。プレゼンテーショナルコンポーネントをコンテナコンポーネントでラップするために必要だった余分なレイヤーを省けます。
メリット
Container/Presentational パターンには多くのメリットがあります。
Container/Presentational パターンは、関心事の分離を促します。プレゼンテーショナルコンポーネントは UI を担当する純粋関数にできる一方、コンテナコンポーネントはアプリケーションの状態とデータを担当します。これにより、関心事の分離を簡単に徹底できます。
プレゼンテーショナルコンポーネントは、データを変更せず、単にデータを 表示 するだけなので、簡単に再利用できます。アプリケーション内のさまざまな用途で、プレゼンテーショナルコンポーネントを再利用できます。
プレゼンテーショナルコンポーネントはアプリケーションのロジックを変更しないため、たとえばコードベースに詳しくないデザイナーでも、その見た目を簡単に変更できます。プレゼンテーショナルコンポーネントがアプリケーション内の多くの場所で再利用されていれば、変更をアプリ全体に一貫して適用できます。
プレゼンテーショナルコンポーネントは通常、純粋関数なので、テストも簡単です。データストアをモックしなくても、渡すデータに基づいてコンポーネントが何をレンダリングするかがわかります。
デメリット
Container/Presentational パターンを使うと、アプリケーションのロジックとレンダリングのロジックを簡単に分離できます。しかし、フックを使えば、Container/Presentational パターンを使わずに同じ結果を得られます。また、ステートレスな関数コンポーネントをクラスコンポーネントに書き換える必要もありません。現在では、状態を使うためにクラスコンポーネントを作成する必要はありません。
React フックと併用しても Container/Presentational パターンは使えますが、小規模なアプリケーションでは、このパターンは過剰になりやすい場合があります。