きゃらめるの備忘録

Salesforceに関してお勉強したことをまとめるブログ。目指せ週1更新~~~!

コンポーネント設計について考えてみた

こんばんは、きゃらめるです。
最後の公開記事の更新が9/1なので、ギリギリ月1回頻度の更新ですね…。
今期は、会社の個人目標にブログの更新を掲げてみました。最低月2回は技術的な更新を目指して頑張ります!
と言っても、本日正式に目標提出したばかりなので、10月がかなりギリギリ…!!
なんとか今週2本、記事をアップできればと思います。

一旦近況報告をしておきますと、前回のブログ更新日となっている9/1から2つ資格を取得しました!

嬉しい!!!!
資格という自分のスキルの証拠が欲しかったので、本当に本当に嬉しいです!!!
次の目標は「Salesforce 認定 Platform デベロッパー」を取ることです。
そして来年の7月…転職して1年でアプリケーションアーキテクトになることを一つの年間目標にしています。
頑張るぞ…!!

本題:LWCのコンポーネントはどんな単位で分割するか

ということで本題です。相変わらずLWC(Lightning Web Component)を触っているので、またLWCの話です。

LWCに限らずですが、コンポーネントを利用する際、どんな単位でコンポーネントを分割するかを決めるのって、結構コツが要る気がしています。
シンプルな画面であれば、1画面に1コンポーネントでもなんとかなると思うのですが、ちゃんとコンポーネント設計を行い、各コンポーネントの役割を単純にしてあげた方が保守性は高くなると思います。
…と言っても、深い階層関係(親・子・孫…のような関係)を作ってしまうと、データのやりとりが煩雑になって逆に保守性が下がってしまう可能性もありますよね。
そこで今回は、私がコンポーネントを使って画面作成する場合に、どんな単位に切り分けているのかを残しておこうと思います。

その1:モーダルウィンドウ

モーダルウィンドウは以下の水色枠で囲われた部分のような要素です。
f:id:calamel_nuts:20201026220720p:plain
この画像ははてなブログの編集画面で「もっと見る」(画像の黄色枠の部分)をクリックすると表示されます。
このようなモーダルウィンドウは、1要素として切り出すのがおすすめです。
モーダルウィンドウはウィンドウというだけあって、別のページを重ねて表示しているような要素なので、親の状態を気にせずに切り出すことが可能な場合が多いです。

ちなみに、モーダルウィンドウの表示・非表示の切替についてですが、表示・非表示を管理するフラグを用意してあげるのがわかりやすいかと思います。

その2:繰り返しのある要素

HTML側でforやiteratorを使う場合や、同じ見た目のまとまりをページ内に複数回記載する場合は、その1要素分をコンポーネント化できないか考えます。
例えば表がある場合に、表の1行分を1コンポーネントとするイメージです。

ただし!今回LWCを使ってみて知ったのですが、LWCで表の1行をコンポーネント化しようとすると単純には行きません。。
コンポーネント化することで、なぜか「1マス」に1行分のデータが入り込んでしまう現象が発生します。
その時はCSSに以下の記述を記載します。

:host {
    display: table-row;
}
その3:複雑なロジックを持つ1要素

これが一番言葉にするのが難しいのですが…私がお仕事で実装した機能を例にすると、「商品型番の最初の何文字かを入力すると、前方一致で商品を検索して候補を表示してくれるルックアップフォーム」を実装した際はコンポーネントに切り分けました。

このフォームを実装する場合、

  • 入力文字から商品の情報を検索
  • 商品候補の表示・非表示を切り替える
  • ユーザが商品を選択した場合は確定した商品名を表示し、入力を受付ないようにする

などなど、細かい制御やデータ取得が必要になります。
これを親ページにべたっと書いてしまうと、親ページのJSが長くなり、保守性が下がります。

コンポーネントに切り分ける意図は、コンポーネントの責務を分離することによる保守性の向上だと思っています。
小さな要素1つであっても、そこに複雑なロジックや複数の状態管理が必要なのであれば、それを分離する旨味は十分あるかなと思います。

これらを全部コンポーネント化すればいいの?

最初にも記載しましたが、無闇にコンポーネントを増やしても旨味は少ないため、
コンポーネント化が必要か否か」を考えることが大事かと思います。
観点としては、
(1)切り分けたコンポーネント単位での処理が行われることがあるか?
(2)コンポーネント内のコンテンツは動的な計算やデータ取得などの加工を行う必要があるか?
(3)コンポーネント化することによって、階層が深くなりすぎないか?
あたりは考えるようにしています。

(1)は例えば、表の1行単位で行の追加機能や削除機能がある場合、1行を1コンポーネントに切り分けた方が楽かもしれません。
特に、その1行の中に複数の要素がある場合、その塊を複製したり、削除したり、という処理は面倒なことが多いです。
複数の要素を1コンポーネントとして扱うことで、一見、親側では「1要素」を複製・削除するように扱うことができるため、管理がシンプルになるかと思います。

(2)については、コンポーネントの中身の複雑性です。
計算やデータ取得など、一手間加えた内容を表示させる場合は、コンポーネントに切り出すことで1つのJSに記述される量を減らすことができます。
逆に、表示しているだけで何も動的に値が変わらない場合は、切り分けても旨味が少ない可能性があります。

(3)については、階層が深くなりすぎる場合、親側でのデータ管理が複雑になりやすいため、避けるようにしています。
コンポーネントから子コンポーネントへデータを渡す場合、子側では@apiプロパティを用意して受け取ることになります。
階層が深くなる場合、例えば「ひ孫でしか使わない情報を、ひ孫を呼び出す孫と、その親の子コンポーネントに渡す」というような感じで、親からバケツリレー形式でデータを渡すことになります。

ただ、LWCは、子から親へのイベント発火部分が、階層構造を感じさせないような作りになっているので、もしかすると階層の深さの影響は少ないかもしれません。
(ここらへん、他の方どうしてるんだろう…)

コンポーネント設計のはじめ方

私は画面を実装する時、画面イメージに線を書いてみて、どこを切り出すか考えます。
例えば、はてなブログの記事の管理画面の、記事一覧をコンポーネント化するならこんな感じかもしれません。
f:id:calamel_nuts:20201026213319p:plain
オレンジの枠は、例でも挙げた、表の1行の繰り返し部分です。
ピンクの枠や水色の枠の部分は、オレンジの枠内にある1つの部品です。
では、ピンクの枠・水色の枠で囲っている部品と、枠で囲われていない部品の差はなんでしょうか?
例えば、タイトルはリンクという機能がついているものの、コンポーネント化は必要ないと判断しました。
これは、aタグ一つで実現できる程度の機能だからです。そういったシンプルな要素をコンポーネント化する旨味はないと判断しました。

ピンクの枠で囲った要素は、クリックするとリストメニューのようなものが出てきます。
つまり、状態の管理やクリック時にイベントが発生するなど、このボタンがクリックされ、メニューが選択されるまでにいくつかの処理が挟まります。
そのため、これはコンポーネントに分けておくことで、親ページ側の記述を減らすことができます。

水色の枠で囲った要素は、正直切り分けるか悩みましたが、元データになっているブログ内容を固定の文字長に切る、という加工が入っているので、コンポーネント分けてもいいかもな〜と思って分けてみました。ここは好みかと思います。

といった具合に、画面イメージに直接書き込むことで、コンポーネント化する部分が目で見て分かりやすくなるので、自分の備忘録としてももちろん、チーム内レビューや設計書として残す場合にも伝わりやすくなるのでオススメです!

おわりに

えらい長くなってしまった。。

こんなことを言ってしまうと元も子もないのですが、結局自分の思ったようにコンポーネントを分けてみて、失敗したり、成功したりすることで、感覚が掴める部分なのかなと思っています。また、場合によってメリットよりデメリットが上回ることもあり得ます。
今回もLWC開発でコンポーネント設計をしてみて、「LWCがこういう仕様なら、ここはコンポーネント分けなきゃよかったな…」みたいなこともありましたので、基礎的な考えは持ちつつ、実装してみて都合が悪かったらやめることも大事かと思います!

今週、もう一本書きたいな〜と思っている内容は、LWCの親子間イベントの渡し方についてです。
上でちょっと触れましたが、あまり階層関係を感じさせないイベントの渡し方になっているなと思ったので、他のフレームワークも調査して、ブログを書ければと思っています!
よかったらそちらもよろしくお願いします!