Container/Presenterパターン
https://scrapbox.io/files/63ff4eb8cb54bb001cd29a7d.png
元記事でも今はhooksが使え、恣意的な分割をせずともロジックとUIを分けることができるのでこのパターンにこだわりすぎないようにと述べられています。もちろん要件や場面にはよりますが、自分の考えとしてはhooksが使える今でもContainer/Presentationalパターンは採用した方がいい思っています。
理由としてはhooksにロジックを抽出したとしてもhooksを呼び出しているコンポーネントがhooksに依存してしまい、先ほど紹介した分割パターンのようにコンポーネントとhooksが2組1セットのようになりコンポーネントの再利用性が低下するからです。
~
hooksを呼び出し、Presentational Componentに適切なデータを渡す橋渡し的な役割がContainer Componentになります。
~
hooksが使えるようになったことでContainer Componentがロジックを切り出すための役割から、ロジックとUIをつなぎ合わせるための役割にシフトしたイメージです。
@dan_abramov: take old “container components”. you know, the ones that pass different data to the “presentational” ones. add a rule: only containers can pass data to other containers. this lets you run the entire container tree ahead of time (on the server or during the build).
that’s RSC.
コンテナ・コンポーネントは、「プレゼンテーション」用のコンポーネントと異なるデータを受け渡すものです。
コンテナだけが他のコンテナにデータを渡すことができる」というルールを追加します。これにより、コンテナツリー全体を前もって(サーバー上またはビルド中に)実行することができます。
というのがRSCです。
koushisa.icon
関連
postsというStoreがあるとする
code:typescript
type Post = {
id: number
name: string
}
const createPostsStore = () => {
let data: Post[] = [
{
id: 1,
name: "foo"
},
{
id: 2,
name: "bar"
}
]
return {
getState: () => data,
setState: (newData:Post[]) => {
data = newData
}
}
}
const posts = createPostsStore()
const usePosts = /* Storeのカスタムフック */
ContainerコンポーネントがStoreにアクセスする
Viewが機能的凝集になるので挙動や振る舞いの理解と改修が容易 code:typescript
// Store<>Presentationインタフェースを提供しているだけ
// 予期せぬ挙動の心配は減る
const PostsContainer: React.FC = () => {
const posts = usePosts()
switch (posts.state) {
case 'loading':
return <Spinner />
case "hasValue":
return <PostsPresenter posts={posts.getValue()} />
case "hasError":
return <Error error={posts.errorOrThrow()} />
}
}
純粋なPresenterコンポーネント
純粋であるため処理の予測可能性が高い
データソースが分離されているので非同期処理を意識しなくていい
code:typescript
type PostsPresenterProps = {
posts: Post[]
}
const PostsPresenter: ReactFC<PostsPresenterProps> = ({posts})=> {
return (/*~*/)
}