3秒でわかる
データ取得と状態管理を引き受け、見た目を持たない側のコンポーネント。表示専用の部品と役割を分けて再利用しやすくするための考え方です。
もう少し詳しく
どういうものか
React のコンテナは、API 呼び出しや状態の保持といったロジックだけを担当し、見た目を持たないコンポーネントです。取得した値を props として渡す先が Presentational(表示用)コンポーネントで、そちらは受け取った値を並べるだけで自分ではデータを取りに行きません。この 2 層に分ける設計を Container / Presentational パターンと呼びます。
なぜ必要か
1 つのコンポーネントが通信も状態も見た目も持つと、見た目を少し変えたいだけの修正で通信処理を読む羽目になります。表示専用に切り出せば、同じ見た目を別のデータ源から再利用でき、Storybook にもテストにも props を渡すだけで載せられます。逆にロジック側は DOM を持たないため、描画を伴わない単体テストが書けます。
具体例
function UserListContainer() {
const [users, setUsers] = useState([]);
useEffect(() => {
<a href="/glossary/fetch-api" class="text-primary font-medium underline underline-offset-2 hover:text-primary-dark">fetch</a>("/api/users").then((r) => r.json()).then(setUsers);
}, []);
return <UserList users={users} />;
}
function UserList({ users }) {
return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}UserList は fetch を知らないので、テストでは配列を渡すだけで検証できます。
つまずきやすいところ
useUsers() のようなフックに寄せる書き方が主流useEffect で通信を始めてしまい、分離が崩れる似た用語との違い
同じ「コンテナ」でも、Docker のコンテナはアプリと依存関係をまとめて隔離実行する仮想化の技術で、まったく別の話です。CSS の .container クラスは中央寄せの幅制限に使う慣習的な名前で、これも無関係です。文脈で判断します。
覚え方
判断の目安としては、通信やタイマー、グローバルな状態に触れているコンポーネントが 1 つでも見た目を持っていたら分離を検討する、という基準が使いやすいです。分離した後で「見た目側のテストに fetch のモックが不要になったか」を確認すると、分け方が正しかったかどうかを判定できます。
「取ってくる係」と「見せる係」に分ける、それだけの考え方です。名前がコンテナである必要すら本質ではありません。