コンポーネントはデータの構造と振る舞いで分けよう

# この記事について
本記事は、フロントエンドのコンポーネント指向フレームワークを使った場合の、コンポーネントの分け方について書かれています。
本記事内での話は、Angularを使用時起こった事ですが、ReactやVue.jsでも変わらないものだと思います。

# はじめに
コンポーネントをどのような観点、どのような粒度で分けるかはそれぞれの流儀、悩み所があり千差万別だと思います。
ある程度の指針のようなもの（プレゼンテーションコンポーネント、コンテナーコンポーネントなど）は聞くことがありますが、ケースバイケースの話になりますので、これを守れば問題ない！と言った銀の弾丸的なものは聞いたことがありません。

しかし「なぜここでコンポーネントを分けたのだろう」「なぜここでコンポーネント分けないんだろう」と感じる、明らかにダメな分け方をしているプ
ロジェクトに遭遇したことがありました。
レイアウトだけを見てコンポーネントを分けていたためであると予想されましたが、本当に大事なのは裏にあるデータの方です。
その駄目な例を元に説明を行います。

# 参考画面
以下は過去遭遇したダメな分け方と感じたプロジェクトの1画面です。
実際は、この3倍近くはフォーム要素の数が多く、条件次第で一部項目が非活性化されるなど複雑な画面です。
![2020-03-31_04h37_57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252031/26989eaf-5896-73d1-1039-46bd71db09f0.png)
行追加ボタンを押すと行が追加されます。
テーブル内の大カテゴリーは小カテゴリーはセレクトボックスになっており、大カテゴリーで選択した値によって小カテゴリーの項目が変わります。
都道府県と市町村区も同様です。
また編集画面ではサーバーサイドからJSONでデータを受け取り、そのデータを編集して全く同じ構造のJSONをリクエストとして投げるような作りになっています。
JSONとして渡されるオブジェクトの構造は以下のようになっています。

```
{
  "項目1": "",
  "項目2": "",
  "項目3": [
    {
      "大カテゴリー": "",
      "小カテゴリー": ""
    },
    {
      "大カテゴリー": "",
      "小カテゴリー": ""
    }
  ],
  "項目4": [
    {
      "都道府県": "",
      "市町村": "",
      "タブ1項目2": "",
      "タブ1項目3": "",
      "タブ1項目4": ""
    },
    {
      "都道府県": "",
      "市町村": "",
      "タブ1項目2": "",
      "タブ1項目3": "",
      "タブ1項目4": ""
    }
  ],
  "項目5": "",
  "項目6": "",
  "項目7": ""
}
```

## どんな風に分かれてたか
![2020-03-31_04h37_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252031/e1e96211-79bb-6878-ffbd-6c41f05113fd.png)

画面上、ブロックになっているところでコンポーネントを作成し、さらにラベルとフォームの組で共通化するコンポーネントが作成されていました。画面の見た目・HTMLのマークアップからコンポーネントを作成したような分け方です。
ぱっと見では綺麗に分かれてるように見えます。
しかし、本当にコンポーネントが欲しいと感じる箇所は別の箇所です。

## コンポーネント化して欲しい箇所

![2020-03-31_04h37_53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252031/54bcf6c4-2d28-99e3-8b55-2d4c242d419a.png)
# 何が違うのか

* 最初に挙げた例での分け方は「レイアウト構造や、レイアウトで繰り返されている箇所」で分けています。
* コンポーネント化して欲しい箇所は「データの構造やデータの繰り返し、データの振る舞い」を見て分けています。

コンポーネント化して欲しい箇所をオブジェクトで見ると以下のような箇所になります。
子となるオブジェクトがあるならば、それはまさにコンポーネント化しやすい箇所です。
![2020-03-31_04h49_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252031/1384cf6c-f049-8f9d-9cbf-8b6ea18bda71.png)

# 何が問題なのか
コンポーネントを作ることによるコストと言うものもあります。
本当にコンポーネントが必要なのか、コンポーネントを作ることで得られることあるのかなどを考え、課題を解決できるものであるべきです。
**レイアウトの共通化だけであれば、コンポーネント化せずともCSSで可能です。**
大カテゴリーと小カテゴリーの連動する処理をどこかに閉じ込められたらすっきりしますよね？「2行目の大カテゴリーを変えたら2行目の小カテゴリーを変える」とか「1個目のタブの都道府県を変えたら1個目のタブの市町村区の項目が変わる」とか、処理の中にインデックスが入り込んだりは嫌ですよね？
こういったロジックの課題はコンポーネント化によって解決されるべきものですが
画面レイアウトだけを見てコンポーネントを分けるとコンポーネント化により解決される課題が見落としてしまう可能性があります。

## カプセル化
コンポーネントを作る際に、「コンポーネントに必要なデータは何か、どんなデータを渡すことになるのか」を考えて作りましょう。
例えば以下のようなコンポーネントを作ると
![da4e57e735a6084043f8af610b3d0836.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252031/d1fae8e8-a398-39a4-edaa-8253f1a8433d.png)

大カテゴリーが変わると小カテゴリーの項目が変化すると言った処理を、親のコンポーネントは意識しないで済むようになります（カプセル化
特にオブジェクトの配列などは、オブジェクトごとにコンポーネント化しやすい箇所です。
オブジェクトのプロパティ同士は強い関連を持ったものだと考えられますので、一つの責務を持たせやすいです。

ちなみに駄目な例のプロジェクトでは、「必要なデータだけを渡す」と言うことを考えてなかったようで
まるっと全体のオブジェクトがコンポーネントに渡されており、他コンポーネントで扱っているプロパティまで読み書きすることが可能な状態になっておりました。。

## 共通化
レイアウトの共通化のためのコンポーネントも当然作られます。
同じHTMLマークアップを何度も書くのはDRYではありません。また同じレイアウトはサイト全体に一貫性が生まれます。
ただし、この用途で使うのは同じデータを扱ってる、コンポーネントに渡すデータが抽象化出来ている場合に限るように思われます。
コンポーネントを作る前に本当にそのレイアウトで共通化されるものであるのか、ルール化できるものであるのか検討すべきです。

例として出した画面内でラべルとテキスト入力のペアのコンポーネントがありますが、
![2020-03-31_04h55_35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252031/301d5906-cbaf-2a6f-3384-50fb3070e774.png)
実際には他画面も含め以下のような様々なケースが存在していました。

* 必須マークを付ける
* 一つのラベルに小さなテキスト入力が2、3個ある
* サブラベルが付く
* 特殊な文言が付く
* 単位が付く

そのたびにコンポーネントにオプションを付けたり、新しいコンポーネントを作ったり、作らなかったりなどに陥っており、破綻してました。
レイアウトの共通化だけであれば、コンポーネント化せずともCSSで可能です
フロントの画面回りは、大人の事情やらでここだけ特別！と言ったことがよく起こります。

# まとめ
フロントエンドと言えども大事なのはデータです。