コンポーネント分割の時に考えていることを改めて言語化した話
この記事は「BEMALab アドベントカレンダー 2024」21日目の記事です。
はじめに
業務の中で、フロントエンドのコーディング規約を作成する機会があり、コンポーネントの分割を文章化しようと試みました。
思いのほか感覚的に判断している部分が多く、あらためて文章化しようとすると難しさを感じました。ある程度その感覚を文章にすることができたので記事にしてみようと思います。
また、これから記載する内容はアプリケーションの性質や、状況によっても変わるものでもあるので、あくまで一個人の考えであることも含め読んでいただけますと幸いです。
コンポーネントの定義
まず、コンポーネントは再利用が可能なUI要素を切り出したものと定義します。
このような定義はフロントエンドフレームワークのドキュメントの中にも言及されています。
したがって、この記事でもコンポーネントは「再利用が可能なUI要素を切り出したもの」と定義します。
切り出しの観点
コンポーネントを再利用可能なUI要素と定義しましたが、どういった粒度感とするべきなのかを言語化していくと以下のようになりました。
本当にコンポーネントとして切り出す必要があるのか
切り出し方の話をする前に、まず切り出そうと考えているコンポーネントは本当に再利用される性質のものか一度立ち止まって考える必要があります。
そもそもコンポーネントは再利用性に着目していることもあり、他で使い回す予定がないものをわざわざ切り出す必要はないと考えます。
デザインなどから明らかに使いまわされる頻度が高いことがわかるのであれば問題ありませんが、そうでない場合や、デザインが固まりきっておらず変更が高頻度で発生する可能性がある場合は一旦切り出さないでおくという判断も必要です。
切り出すのは同じコードを色々なところで使うようになってきてからでも遅くありません。
時期尚早な最適化は害悪という考え方もあり、Google も早い段階での DRY 原則の適用は行わない方が良いというブログを公開しています。
Google Testing Blog: Don't DRY Your Code Prematurely
状況が流動的であればあるほど、変更が多発したり、要素そのものが消えたりする可能性も考えられるため、逆に変更コストが高くついてしまう場合もあります。
作ろうとしているものが UI ライブラリのようなものであれば、再利用可能な UI を作ることが開発の目的となるため、積極的にコンポーネント化する必要があると言えるでしょうが、そうでない場合はまず本当に切り出す必要があるかを考えるところから始めることも必要でしょう。
UIとして機能するために必要最小限の機能を持っていること
再利用可能であるためには1つのコンポーネントに多くの機能を持たせない方が良いことが多いです。切り出したコンポーネントが扱うべき機能を持てば持つほど使い回しがしにくくなります。特に、切り出すコンポーネントがUI(見た目が重要なもの)であれば使いまわされる可能性が高いのでよりシンプルにしておく方が良いと考えます。
ボタンをコンポーネント化することを例として考えると、ボタンが持つべき責務は次のようになると思います。
- アプリケーションにおけるボタンのスタイルを持っている
- HTML 要素としての Button が持っている attribute を受け取れる(要件にもよるが)
- そのほか、その Button がアプリケーションのUIとして持つべきデザインの切り替えを行うための props
極端な例えではありますが、このボタンコンポーネントが特定の API エンドポイントへのフェッチ処理などまで持っていた場合このボタンは汎用的とは言えなくなります。
ボタンはクリックされ、コールバックが実行できれば良いものとすれば、ボタンを使う側で自由にロジックと組み合わせることができるため、より再利用しやすくなります。
コンポーネントとして切り出すときに心がけていること
コンポーネントを切り出す範囲を決め、実際に切り出した後に気を付けていることを次にまとめていきます。
コンポーネントのスタイル
コンポーネントにつけるスタイルは、切り出したコンポーネントに必要最小限とし、レイアウトに関するスタイル(margin など)は付けないようにします。
コンポーネントの配置に関するスタイルは使う側で制御するようにした方がより再利用しやすいものになります。仮にデフォルトでそういったスタイルを持ってしまった場合、都度スタイルの上書きを行う必要が出てくる可能性があります。
コンポーネント自体にそういったスタイルの上書きができるような機能を持たせてしまうことも戦略としてありかもしれませんが、個人的には外部から直接スタイルを注入することは結果的に細かい差異をもつ多くのコンポーネントを産むことになり、コンポーネント化の意味が薄れると考えているので可能な限り避けています。
props
props の命名は一定の規則に沿ったものにすることを意識しています。例えばリストのようなコンポーネントを作成する場合は、リストに表示する要素の配列は items と命名して、同じような意味合いを持つ props であれば統一するようにしています。
このような一定の規則があることで使うときの予測がしやすくなり、見通しが良くなると個人的に考えているためです。
また、イベント系の props は 「on~」 と統一するなどイベント系のコールバックであることが props から予測しやすいように心がけています。
また、コンポーネントにあまりに大量の props が必要になってしまった場合は、1つのコンポーネントに多くの責務を持たせすぎている可能性があります。このため、props の数もコンポーネント分割の指標とすることができます。
さいごに
今までコンポーネント分割をするときに自分がどう判断しているのか、言語化できておらず感覚に近い形で行っている部分がありました。
まだ言語化できていない観点もあり、細かい判断基準なども今後言語化していければと思います。
この記事を書いた人
関連記事
- Astroをフロントエンドフレームワークとして利用する
Hideki Ikemoto
- React Redux: 毎回新しい参照を返す select...
Daisuke Yamamura