🕌

いつコンポーネントを複数に分割するのか?

2023/02/07に公開

Reactアプリケーションを単一のコンポーネントとして書けることを知っていましたか?単一の巨大なコンポーネントに全てを実装するのを技術的に止めるものは何もありませんが、あなたの関数は巨大になるでしょう。ステートと副作用のためのフックが大量に必要になりますが可能です。

もし、上記の事を行おうとするといくつかの問題に直面することになります。

  1. ステートが変化する度にアプリケーション全体が再レンダリングするので、パフォーマンスが恐らく低下するでしょう。
  2. コードの共通化/再利用が困難になるでしょう。少なくとも、クラスコンポーネントにした場合、ランタイムエラーをハンドリングするためにcomponentDidCatchを使いたい場合はそうしなければならないかもしれません。react-error-boundaryを使用してフックを使えるようにすれば、かなり楽になると思います。話が逸れましたね...。
  3. ステートとイベントハンドラーのどの部分が、JSXのどの部分に対応しているかを知る事は頭を痛めます。そして、バグを追跡するのが困難になります。(これは関心毎の分離の利点の一つです)
  4. テストは100%統合されることになります。必ずしも悪い事ではありませんが、エッジケースをテストし、テストしようとする部分とそのエッジケースを分離し続けるのは非常に難しいです。ですので、過剰なテスト(E2Eテストによくある間違え)に悩まされるでしょう。
  5. 複数のエンジニアとコードベースで作業をするのは、ゾッとします。gitの差分やコンフリクトをイメージできますか?
  6. サードパーティのコンポーネントライブラリを使用するのは...うーん...無理かな?単一コンポーネントに全てを実装するなら、サードパーティのライブラリはそれに一致しません!もし、使えたとしてもreact-emotionのような高階コンポーネントはどうなりますか?許されません!(それならcss propを使うべきです)
  7. 宣言型コンポーネントAPI内で、命令型抽象/APIをカプセル化する事ができない。つまり、命令的なAPIは巨大な単一コンポーネントのライフサイクルフックに散らばり、コードを追うのが難しくなります。(フックを使っている場合は、関連するフックをまとめて管理すればマシになります。しかし、それでもちょっとした悪夢です)

これらが、私達がカスタムコンポーネントを書く理由です。これらの問題を解決することができるのです。

Best ways/patterns to split app into componentsという質問を受けた事があります。それに対する私の答えは、上記のような問題が発生した時は、コンポーネントを複数の小さなコンポーネントに分割する時です。その前にやる事ではありません。1つのコンポーネントを複数のコンポーネントへ分割する事を抽象化と呼びます。抽象化はとても良いが、全ての抽象化にはコストが掛かります。そのため、コストとメリットを認識した上でやる必要があります。

Duplication is far cheaper than the wrong abstraction. — Sandi Metz

ですので、コンポーネントが返すJSXが凄く長くなっても心配しないでください。JSXはコンポーネントによって提供された宣言的なAPIを使用するJavaScriptの式の集まりに過ぎないことを忘れないでください。そのようなコードでうまくいかないことはほとんどありませんし、そのままにしておく方が小さなコンポーネントに分割して、あらゆるところでProp Drillingを始めるよりもずっと単純なのです。

結論

コンポーネントを小さく分割するのは自由ですが、実際に問題が発生するまでは肥大化していくコンポーネントを恐れないでください。未熟な抽象化を維持するよりも、コンポーネント分割が必要になるまで、肥大化していくコンポーネントを維持していく方がより簡単です。Good luck!

Discussion