Container・Presentationパターン

概要

フロントエンドのコンポーネントにおけるデザインパターン。

データ取得・副作用・アプリケーション状態の管理とUI表示の責務を分けることで関心の分離を実現する。

コンポーネントのパーツの再利用性を高めることができる。

使い方

bad

const UserProfile = () => {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    fetchUser().then(setUser);
  }, []);

  if (!user) return <div>Loading...</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
};

表示と取得・状態が全部同じコンポーネントに集まってる。

取得ロジックのテストや状態管理のテストなどがやりにくい。

また、複雑化したときにコンポーネントが肥大化しやすい。

good

const UserProfileContainer = () => {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    fetchUser().then(setUser);
  }, []);

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

const UserProfile = ({ user }: { user: User | null }) => {
  if (!user) return <div>Loading...</div>;
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
};

コンテナ: 状態や副作用、ロジックを持つコンポーネント プレゼンテーション: 状態や副作用、ロジックを持たないUI表示に特化したコンポーネント(厳密には、表示や見た目のためだけの状態は持っても良い)

使い所

UIとロジックを分けることで再利用性が上がる 状態を持たないため純粋にUIだけを評価できる → テストが書きやすくなる

  • コンテナ: vitestなどのテストでロジックのテストが書きやすい
  • プレゼンテーション: StorybookなどのテストでUIのテストが書きやすい

レイヤードアーキテクチャやDDDなどのプレゼンテーション層などとの違い

レイヤードアーキテクチャやDDDなどでもプレゼンテーション層などで表示とロジック(とその他)を分けているが、 それらとの違いは? → そもそも考えているスコープが違う

アーキテクチャの方がもっと広い → アプリケーション全体において ビジネスロジック と コード を繋ぎ合わせる

コンテナプレゼンテーションパターン → フロントのコンポーネントにおける表示とロジックの分離

参考