日本語訳: Presentational and Container Components


勝手に翻訳シリーズ

元の記事

https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0

Presentational and Container Components

Bismuth
There’s a simple pattern I find immensely useful when writing React applications. If you’ve been doing React for a while, you have probably already discovered it. This article explains it well, but I want to add a few more points.
とても有益でシンプルなパターンを、Reactを書いているときに見つけました。もちろんあなたもReactをある程度書いたことがあれば、多分同じようなことを既に発見されていることでしょう。Thisこの記事も同じことについて説明しています。ですが、さらにいくつか新しいことを紹介できるとおもいます。
You’ll find your components much easier to reuse and reason about if you divide them into two categories. I call them Container and Presentational components* but I also heard Fat and SkinnySmart and Dumb, Stateful and PureScreens and Components, etc. These all are not exactly the same, but the core idea is similar.

ご存知だと思いますが、コンポーネントをうまく再利用するためには、そしてうまく整理するためには、コンポーネントを2種類に切り分けると良いのです。その二つを私はContainer ComponentとPresentational Componentと名付けることにしました。ですが人によってはFatとSkinny、SmartとDumb、StatefulとPure、ScreensとComponentsと、様々な呼び方をしています。これらは全てが完全に同じものではありませんが、中心的な役割は同じです。

My presentational components:

私がPresentationalと名付けたコンポーネントの特徴は以下の通りです。

  • Are concerned with how things look.
  • どのような見た目になるか、ということと関連づいている
  • May contain both presentational and container components** inside, and usually have some DOM markup and styles of their own.
  • PresentatinalコンポーネントとContainerコンポーネントの両方を内部に持つことができ、たいていの場合は自分自身のためのDOMマークアップとスタイルを持つ。
  • Often allow containment via this.props.children.
  • たいていの場合は、コンテンツをthis.props.childrenを用いて保持している。(訳注:公式参照 コンポーネントの<Component>ここ<Component>ここの部分入れらたものがthis.props.childrenで参照できる部分。通常、これは自動的にレンダリング時に参照され、内部で保持され、表示される。)
  • Have no dependencies on the rest of the app, such as Flux actions or stores.
  • アプリケーションの他の部分に対して依存している部分が一切ない。例えばFluxのactionやstoreに依存している部分がない。
  • Don’t specify how the data is loaded or mutated.
  • dataがどのようにロードされるか、もしくは変更されるか、といったことは定義しない。
  • Receive data and callbacks exclusively via props.
  • dataやコールバックは外部からprops経由で受け取る。
  • Rarely have their own state (when they do, it’s UI state rather than data).
  • 独自の状態を保つことは基本的にはない。(あるとすれば、UIの状態を持つ場合であってDateではない。)
  • Are written as functional components unless they need state, lifecycle hooks, or performance optimizations.
  • Functional コンポーネントとしてかかれる。状態やライフサイクルフック、パフォーマンス調整が必要とされない限りは。
  • Examples: Page, Sidebar, Story, UserInfo, List.
  • 例としては、Page, Sidebar, Story, UserInfo, Listなどがあげられる。

My container components:

私がContainerと名付けたコンポーネントの特徴は以下の通りです。

  • Are concerned with how things work.
  • どのように機能するか、ということと結びついてる。
  • May contain both presentational and container components** inside but usually don’t have any DOM markup of their own except for some wrapping divs, and never have any styles.
  • PresentatinalコンポーネントとContainerコンポーネントの両方を内部に持つことができるが、たいていの場合は自分自身のためのDOMマークアップとスタイルを「持たない」。
  • Provide the data and behavior to presentational or other container components.
  • データと挙動を、Presentationalコンポーネントや、他のContainerコンポーネントに対して提供する。
  • Call Flux actions and provide these as callbacks to the presentational components.
  • Fluxのアクションをcallしたり、アクションをコールバックとしてPresentatinalコンポーネンへ提供する。
  • Are often stateful, as they tend to serve as data sources.
  • たいていの場合は状態を持ち、データの源としての役割を担う。
  • Are usually generated using higher order components such as connect()from React Redux, createContainer() from Relay, or Container.create() from Flux Utils, rather than written by hand.
  • higher order componentを用いることで生成される。例えばReact Reduxのconnect()やRelayのcreateContainer()やFlux UtilsのContainerCreate()である。
  • Examples: UserPage, FollowersSidebar, StoryContainer, FollowedUserList.

I put them in different folders to make this distinction clear.

私はこの二つのコンポーネントを、異なるフォルダーに配置することで、明確に区別することにしています。

(訳注:ということは、Reduxのconnect()を使う層は、基本的にContainer Component。connectはなぜ使うかといえば、state,reducer,actionとpresentationalコンポーネントを、Containerコンポーネント内で結びつけるため。presentationalコンポーネントとactionをロードして、connectでstate,dispatchと結びつける。するとpresentationalコンポーネントにstate,dispatchが渡されるので、presentationalコンポーネントがそれを使えるようになる。connectで結びつけて作った新しい高次階層コンポーネントをexportし、これを他のコンポーネントから呼び出して使う。)

Benefits of This Approach

このアプローチの利点

  • Better separation of concerns. You understand your app and your UI better by writing components this way.
  • 役割の分割をうまくできる。コンポーネントをこう書くことで、アプリケーションとUIがどうなっているのか、より理解しやすくなる。
  • Better reusability. You can use the same presentational component with completely different state sources, and turn those into separate container components that can be further reused.
  • 使い回しが効く。Presentationalコンポーネントを全く異なるStateを持つ状況において使用することができる。また異なるContainerコンポーネントの中で使用することができる。
  • Presentational components are essentially your app’s “palette”. You can put them on a single page and let the designer tweak all their variations without touching the app’s logic. You can run screenshot regression tests on that page.
  • Presentatinalコンポーネントは、本質的にはアプリケーションの「パレット」である。ページのどこにでも配置することができ、デザイナーがアプリのロジックに触ることなく変更することができる。
  • This forces you to extract “layout components” such as Sidebar, Page, ContextMenu and use this.props.children instead of duplicating the same markup and layout in several container components.
  • こうすることで「layout Component」を強制的に取り除くことになるので、同じマークアップやレイアウトを様々なContainerコンポーネントになんども複製する必要がなくなる。this.props.childrenを使えばいい。

Remember, components don’t have to emit DOM. They only need to provide composition boundaries between UI concerns.

コンポーネントは必ずしもDOMを出力する必要はない、という点に注意してください。第一の役割は、コンポジションにおける境界を定義することにあります。

Take advantage of that.

どの段階でContainerを導入すべきか

When to Introduce Containers?

I suggest you to start building your app with just presentational components first. Eventually you’ll realize that you are passing too many props down the intermediate components. When you notice that some components don’t use the props they receive but merely forward them down and you have to rewire all those intermediate components any time the children need more data, it’s a good time to introduce some container components. This way you can get the data and the behavior props to the leaf components without burdening the unrelated components in the middle of the tree.

まずはPresentationalコンポーネントだけでAppを書き始めることをお勧めします。そうしていくと、最終的には、あまりに多くのpropsを中間層のコンポーネントに渡している、と感じることにはなると思います。propsを受け取ってはいるものの、用いておらず、単にさらに下部のコンポーネントへと渡しているだけのコンポーネントがあるような状況が生まれた場合が、Containerコンポーネントを導入するのに最適な段階でしょう。そうすれば、データや挙動のためのpropsを各コンポーネントに与えるために、関係のないコンポーネントを中間層に挟む必要がなくなります。

This is an ongoing process of refactoring so don’t try to get it right the first time. As you experiment with this pattern, you will develop an intuitive sense for when it’s time to extract some containers, just like you know when it’s time to extract a function. My free Redux Egghead series might help you with that too!

これはリファクタリンの際に継続的におこなうことなので、最初から行う必要はありません。このパターンで書く際に経験していることだと思いますが、いつコンテイナーを導入するのかということを感じ取るセンスを育む必要があります。これはちょうど、どの段階でFunctionにまとめるべきなのかを判断するセンスと似ています。 free Redux Egghead series も参考になるはず!

他の違い

Other Dichotomies

It’s important that you understand that the distinction between the presentational components and the containers is not a technical one. Rather, it is a distinction in their purpose.

presentationalコンポーネントとcontainerコンポーネントの違いは、技術的なものというよりは、その目的による、という点を理解することが重要です。

By contrast, here are a few related (but different!) technical distinctions:

  • Stateful and Stateless. Some components use React setState() method and some don’t. While container components tend to be stateful and presentational components tend to be stateless, this is not a hard rule. Presentational components can be stateful, and containers can be stateless too.
  • Stateful and Stateless.  ReactのsetState()を使うコンポーネントもあれば、使わないコンポーネントもあります。ContainerコンポーネントはStateを持つ傾向にあり、Presentationalコンポーネントがstateを持たない傾向にありますが、とはいえこれは厳密なルールではありません。Presentationalコンポーネントがstateを持つこともありますし、ContainerコンポーネントがStateを持たないこともあります。
  • Classes and Functions. Since React 0.14, components can be declared both as classes and as functions. Functional components are simpler to define but they lack certain features currently available only to class components. Some of these restrictions may go away in the future but they exist today. Because functional components are easier to understand, I suggest you to use them unless you need state, lifecycle hooks, or performance optimizations, which are only available to the class components at this time.
  • Classes and Functions. React 0.14から、コンポーネントはクラスとしてもファンクションとしても宣言できるようになりました。ファンクショナルコンポーネントは宣言がよりシンプルですが、クラスコンポーネントにしかない機能を持っていません。そういった制限が将来撤廃されるかもしれませんが、今のところは存在します。ファンクショナルコンポーネントは把握するのが簡単ですので、ステイト/ライフサイクルフック/パフォーマンスの調整といったクラスコンポーネントだけにしかない機能が必要でない場合には、ファンクショナルコンポーネントを使うことを推奨します。
  • Pure and Impure. People say that a component is pure if it is guaranteed to return the same result given the same props and state. Pure components can be defined both as classes and functions, and can be both stateful and stateless. Another important aspect of pure components is that they don’t rely on deep mutations in props or state, so their rendering performance can be optimized by a shallow comparison in their shouldComponentUpdate() hook. Currently only classes can define shouldComponentUpdate() but that may change in the future.
  • Pure and Impure. コンポーネントのうち、同じpropsと状態を与えれば、同じ結果を返すものを、Pureコンポーネント、と言ったりします。ピュアコンポーネントは、クラスでもファンクションでもどちらでも定義することができます。またステイトを持つことも持たないこともできます。(残りの部分わからず)

Both presentational components and containers can fall into either of those buckets. In my experience, presentational components tend to be stateless pure functions, and containers tend to be stateful pure classes. However this is not a rule but an observation, and I’ve seen the exact opposite cases that made sense in specific circumstances.

PresentationalコンポーネントもContainerコンポーネントも、そうではないもう一方のコンポーネントになる可能性が常にあります。私の経験では、Presentatinalコンポーネントはstatelessなピュアファンクションであり、Containerコンポーネントはstateを持つピュアクラスであることが多いようです。とはいえこれはルールではなくて、あくまでもそういうことが多いというだけなので、過去には全くの反対のケースで、しかも合理的、という状況もありました。

Don’t take the presentational and container component separation as a dogma. Sometimes it doesn’t matter or it’s hard to draw the line. If you feel unsure about whether a specific component should be presentational or a container, it might be too early to decide. Don’t sweat it!

PresentationalコンポーネントとContainerコンポーネントの区別を、教義のように考えないようにしてください。時にはそんなことを考える必要がない時もありますし、区別をすることが難しい場合もあります。どちらにすべきか確信が持てない場合には、それを決断するにはまだ早いのかもしれません。そんなことは気にしないで!

Example

This gist by Michael Chan pretty much nails it.

Further Reading

Leave a Reply

Your email address will not be published. Required fields are marked *