いいから黙ってpropsのコールバックは`on`から始めておけ

# この記事について

弊社（株式会社ゆめみ）のフロントエンドで開催された「私の強い思想を聞いてくれLT会」という勉強会で発表した内容をせっかくなので記事として残しておこうと思い、執筆しました。

勉強会の全体像については、開催してくれた @taka10257 さんによって記事にされているのでぜひこちらをご覧ください。
[私の強い思想を聞いてくれLTという勉強会のログ](https://qiita.com/taka10257/items/3e2c546e84b819a63b0c)


# 本題： いいから黙ってpropsのコールバックは `on` から始めておけ

## 結論： （いいから黙って強い思想はまず結論から述べておけ）

とにかくpropsの関数の命名は **`on`** から始めるようにしましょう。
そうすれば、適切なpropsの命名、コンポーネントの良い設計に自然となっていきます。

コンポーネントに渡すコールバック関数は親コンポーネントと子コンポーネントのインタフェースです。
子は親の実装を意識しなくてよくて、親は子の実装を意識しなくていい状態が理想です。

子コンポーネントの何かしらのイベントによってコールバックが実行されるので、基本的には常に `onHogeEvent` という **`on`** から始まるpropsの命名にできるはずです。

React や Vue.js の公式のドキュメントでも「関数をpropsに渡せるよ〜」というような紹介ではなく、「コンポーネントで起こったイベントにハンドラーを渡せるよ〜」というように紹介されています。

https://react.dev/learn/responding-to-events

https://ja.vuejs.org/guide/components/events

## `on` から始めていない時

`on` から始めていない時、どういった問題が起こってくるかというのをいくつかのアンチパターンを示しながら紹介したいと思います。

:::note warn
LT開始数時間前に考えたアンチパターンなので最適なパターンではないことをここに保険としてかけておきます。
:::

### `setHoge` のようなpropsを定義したケース

すごくズボラな実装をしたいとき、以下のようにしてしまうことがあります。

```tsx
const [hoge, setHoge] = useState()
<Hoge setHoge={setHoge}>
```

親コンポーネント側は `setHoge` というstateのセッター関数を渡すだけでいいのですごく楽ですね。

これが子コンポーネントで使われるケースについて考えてみると、面倒なことが起きてきます。

```tsx
const Hoge = ({ setHoge }) => {
  const handleSubmit = (hoge) => {
    setHoge(hoge)
  }
  const handleFetchCompleted = (hoge) => {
    setHoge(hoge)
  }
  // ...
}
```

`setHoge` は子コンポーネントのどこででも呼ばれる可能性があります。

`hoge` stateが更新されていることを前提としている機能を親コンポーネントで実装するときに、どこでそのstateが更新されているか確認するために子コンポーネントを見に行かないといけなくなります。

また、`fuga` というstateも更新したくなったら `setFuga` もpropsとして渡さないといけ無くなってしまいます😭

stateのセッター関数を直接コンポーネントのpropsとして渡すような実装は絶対に避けた方が良いです。

---

### `saveSometing`, `resetSometing` のような特定の動作をする関数をpropsとして定義したケース

こういう実装をしたとします。

```tsx
<Hoge saveSomething={saveSomething} resetSomething={resetSomething}>
```

```tsx
const Hoge = ({ saveSomething, resetSomething }) => {
  const handleSubmit = () => {
    // ...
    saveSomething(something)
  }
  const handleCancel = () => {
    // ...
    resetSomething()
  }
  // ...
}
```

特定の動作をする関数をpropsとして受け取っているので、

- コンポーネントは親コンポーネントからどういう関数を渡されるかに依存した実装になってしまう
- 親コンポーネントからは渡した関数がどこで呼び出されているかわからない

という2つの問題を抱えることになります。

例えば、以下のコードのように渡している関数が複数の箇所で呼ばれているかもしれません。

```tsx
const Hoge = ({ saveSomething, resetSomething }) => {
  const handleSubmit = () => {
    saveSomething(something)
    resetSomething()
  }
  const handleCancel = () => {
    resetSomething()
  }
  const handleOutsideClick = () => {
    resetSomething()
  }
  // ...
}
```

もし渡している `resetSomething` という関数の機能を変更したいとなったときに、子コンポーネントの実装を確認して呼び出されている場所を確認しながらバグが起こらないように注意しながら更新することになります。

以下のようにイベントベースでpropsを命名しておけば、そのイベントが起こったときに呼び出されることが明確になります。

実行する処理が同じでいいのであれば同じ関数を渡して仕舞えば良いだけなので変更にも耐えやすいコンポーネントとして実装することができます。

```tsx
const handleSubmit = () => {
  saveSomething()
  resetSomething()
}

const handleCancel = () => {
  resetSomething()
}

const handleOutsideClick = () => {
  resetSomething()
}

<Hoge
  onSubmit={handleSubmit}
  onCancel={handleCancel}
  onOutsideClick={handleOutsideClick}
  // or
  // onOutsideClick={handleCancel}
>
```

```tsx
const Hoge = ({ onSubmit, onCancel, onOutsideClick }) => {
  const handleSubmit = () => {
    // ...
    onSubmit(something)
  }
  const handleCancel = () => {
    // ...
    onCancel()
  }
  const handleOutsideClick = () => {
    // ...
    onOutsideClick()
  }
  // ...
}
```

こうすることで、 `Hoge` というコンポーネントのインタフェースが明確になりますし、`Hoge` コンポーネントでイベントが起こった時に実行される処理の内容は親コンポーネント側でハンドリングできるので、お互いに疎結合な良いコンポーネントになります。

### （余談） `handleHogeEvent` っていうprops名はよくないの？

そんなのわかってるよって人も多いと思うのでトグルの中に突っ込んでおきます。

<details><summary>`onHogeEvent` と `handleHogeEvent`の命名について</summary>

たまに、

```tsx
const handleHogeEvent = () => {}
<Hoge handleHogeEvent={handleHogeEvent}>
```

というような実装をしているケースを見ることがあります。

これは、

```tsx
const handleHogeEvent = () => {}
<Hoge onHogeEvent={handleHogeEvent}>
```

のようにしておくべきです。

`onHogeEvent` と `handleHogeEvent`はどう違うのでしょうか？

`onHogeEvent` は `HogeEvent` というイベントが起こったときに、渡されたコールバック関数を実行しますよというのを示しているコンポーネントのpropsの命名です。

`handleHogeEvent` は、 `HogeEvent` というイベントをハンドルするためのハンドラー関数だというのを示した命名です。

この2点を覚えて適切に使っておけば、大丈夫です！

これからは `handleHogeEvent` をprops名にするのはやめて、 `onHogeEvent` をprops名にしましょう。

</details>

## まとめ

- とりあえずonから始まる命名を考えてみる。
- コンポーネントに関数を渡すときは、イベントハンドラーとして渡す。

この2点を意識することで、かなり良いコンポーネント設計になると思います。

ぜひpropsに関数を渡したくなったときに、 **`on`から命名をはじめられないか** というのを最初に考えるようにしてもらえると嬉しいです。

## LT後の学び

LT中のコメントでESLintのプラグインでonから始まる命名にするように矯正するルールがあることを教えてもらいました。
これはとても便利ですね。

これはぜひ全プロジェクトに入れましょう。（強い思想）

（ちなみに筆者はまだ使ったことないです😅）

https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md

# 最後に

面白いイベントを社内で企画してくれた @taka10257 さんの記事をもう一度紹介させていただきます。
[私の強い思想を聞いてくれLTという勉強会のログ](https://qiita.com/taka10257/items/3e2c546e84b819a63b0c)

「私の強い思想を聞いてくれLT会」という企画に対してすぐに話したいことがたくさん出てくるメンバーもたくさんいて、とても面白い勉強会でした。
2回目も企画するかもしれないとのことなので、楽しみにしていたいな〜と思います。

今回の記事の内容はあくまで私の個人的な思想も含んだものなので、異論もあると思います。
個人的には人の強い思想を聞き入れつつ、自分の強い思想を育んでいくのが良いと思ってますので、ぜひ皆さんの強い思想も聞いてみたいと思います。
その際は、ゆめみに入社して話していただくか、ぜひ記事にしていただければと思います。

最後まで読んでいただきありがとうございました。