イベントハンドラの関数名についての雑記

今日のXでは、イベントハンドラの関数名についての話題を見ました。[元の投稿はこちらです](https://twitter.com/plus_one_masaki/status/1750705805152944273)。要するに、次の`handleClick`のような`handleイベント名`という命名は良くないということです。

```jsx
const handleClick = () => { ... };
<button type="button" onClick={handleClick} />
```

これについて少し考えたので、せっかくなのでアウトプットしておくことにしました。

## この記事の目的

- 考えたことを頭の中に残しておくのがもったいないので、文章の形にしておく。
- 色々な意見が出る問題であり、ベストな答えは無いので各々好きにすれば良いということを、読者に理解してもらう。
- とはいえ、筆者の考えに納得して同じ考えの人が多いと嬉しいので、なるべく納得してもらう。

## 筆者の考え

自分の考えとしては、むしろhandleClickいいじゃん！　どんどん付けようぜ！　派です。

特に、[イベントオブジェクト](https://developer.mozilla.org/en-US/docs/Web/API/Event)を取り扱う必要がある場合のことを考えてみましょう。

```tsx
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
  // 今どきpreventEventを使う場面も少ないが例として
  event.preventDefault();
  // ...
  addUser();
};
<button type="button" onClick={handleClick} />
```

このような場面で、`handleClick`を経由せず直接 `addUser` というドメインロジック的な名前を持つ関数を作るのは無理があると思います。

```tsx
// ドメインロジックにイベントというUIの都合が食い込んでいるんですけど？
const addUser = (event: React.MouseEvent<HTMLButtonElement>) => {
  // ...
};
```

少なくともこのケースがある時点で、「DOMイベントを取り扱う」という責務の関数が必要なのであり、そのような関数に対して`handleClick`のような命名は悪くないと思います。つまり、「`handleClick`のような命名はすべからく避けるべきである」という主張は難しいと思います[^subekaraku]。

[^subekaraku]: 余談ですが、「すべからく」と書くと、誤用ではなくても誤用警察が現れるのが日常茶飯事です。これは本来の意味での使い方のつもりですが、果たして誤用警察は現れるでしょうか。

### イベントオブジェクトを使わない場合は？

イベントオブジェクトを別に使わない場面も多いでしょう。そのような場面で、次のように`handleClick`を定義するのでしょうか？

```jsx
const handleClick = () => {
  addUser();
};
<button type="button" onClick={handleClick} />
```

筆者であれば、さすがにこれはやりません。直接`addUser`を`onClick`に渡します。

```jsx
// さすがにこうする
<button type="button" onClick={addUser} />
```

つまり、「絶対に`handleClick`という命名にすべき」というのも過剰だと筆者は感じています。

このようなコードは、筆者は次のように脳内変換しながら見ています。

```jsx
// 脳内変換後
const handleClick = addUser;
<button type="button" onClick={handleClick} />
```

あくまで脳内でこのように解釈し、実際のコードとしては直接`addUser`を渡すのが筆者の普段のやり方です。

最近の言語の多くは関数を第一級オブジェクトとして扱える（関数を変数に入れたり、別の関数に渡したりできる）ようになっていますが、このような解釈ができるのもその恩恵を受けていると考えるとすこし面白いですね。

ただ、実際にはことはこれほど単純ではありません。実際のUIであればローディングやエラー処理も挟まります。例えば、Reactの`useTransition`を使っている場合は次のようにすることもあるでしょう。

```jsx
const [isPending, startTransition] = useTransition();

const handleClick = () => {
  startTransition(() => {
    // ...
    addUser();
  });
};

<button type="button" disabled={isPending} onClick={handleClick} />
```

このようなUI都合のロジックを`addUser`に詰め込むのもやはり命名的に無理があります。そうなると、`handleClick`のような層が必要になる場面は現実的に多いでしょう。

ただ、UIの関心ごとを色々詰め込むと`handleClick`という名前が適切かどうかはそれはそれで怪しくなってきます。`handleClick`でも`addUser`でもない名前が適切になる場面が多いかもしれません。

## コンポーネントの親子関係について

コンポーネントを細分化すると、イベントハンドラが親から子へバケツリレーされるケースもあるでしょう。

そのような場合、`onClick`のようにイベント名がついたpropsはかなりDOMに近い関心事の概念ですから、末端のコンポーネントで隠蔽されるべきです。

例えば、ユーザー追加ページのコンポーネント`AddUserPage`を考えます（フォームの場合submitイベントを取り扱った方がいいので`onSubmit`を例に用います）。

```jsx
const AddUserPage = () => {
  const handleSubmit = () => {
    // ...
  };
  return (
    <form onSubmit={handleSubmit}>
      ...
      <button type="submit">add user</button>
    </form>
  );
};
```

いわゆるContainerコンポーネント的なやつがあって実際のユーザー追加処理は`AddUserPage`のさらに親コンポーネントで行いたい場合、`AddUserPage`のpropsはどう命名すべきでしょうか。

筆者ならば、ここで名前を変えます。

```jsx
const AddUserPage = ({
  // ここでonSubmitにはしたくない
  // onSubmit,
  onAddUser,
}) => {
  return (
    <form onSubmit={onAddUser}>
      ...
    </form>
  );
};
```

つまり、`AddUserPage`がDOMレベルの関心事（フォームがどのように実装されているか？　送信はどう検知するか？）を内包しているので、`AddUserPage`を使う側はそのことを知る必要はありません。だから、DOMレベルのイベント名が現れた`onSubmit`にはしたくありません[^page_submit]。

逆にいえば、`onClick`や`onSubmit`といったイベント名が直接現れるpropsは、DOMやUIを直接的な責務とする、末端のコンポーネント（あるいはネイティブのHTML要素）にのみ現れるものなのです。そうなると、親としてもそれらのpropsに渡すのは、抽象度の高い処理ではなく、イベントの処理を直接担当する関数であるべきだと考えます。そのような関数の命名として`handleClick`は適切なんじゃないかなあと思います。

[^page_submit]: `AddUserPage`ではなく`AddUserForm`みたいな粒度のコンポーネントであれば、submitはイベント名ではなくただの動詞だよということで`onSubmit`でもいいかもしれません。今回は`Page`という粒度のコンポーネントにしたので、一般の動詞としても`onSubmit`は不自然でしょう。

## React？

あまり話の本筋とは関係ありませんが、元の投稿では`handleClick`のような命名は「Reactの慣習」として説明されています。筆者はReactを使い出す前から`button.addEventListener("click", handleClick);`とかやってきた人間なのでReactの慣習と言われてもピンと来なかったのですが、どうもReactにおいてこのような命名が行われがちと感じる人は一定数いそうな雰囲気を感じます。

実際、[Vueの公式ガイド](https://vuejs.org/guide/essentials/event-handling.html)と[Reactの公式ガイド](https://react.dev/learn/responding-to-events)を見比べると、Reactでは`handleClick`のような命名をしており、Vueはしていません。（Vueの方は`function greet(event)`のような命名になっており、前述のとおりそれはどうなのとちょっと思いますが）。文化的な差も無いわけではなさそうです。

この点に関して筆者としてお伝えしたいのは、公式はあくまで公式だからあなたの考えを優先していいよということです。Vueで`v-on:click="handleClick"`してもいいし、Reactで`onClick={addUser}`しても良いのです。あなたが納得して、そのやり方を好むならばですが。そもそも、関数の命名なんてUIライブラリの使い方とは別に関係ないのですから（Reactのフックのような例外はありますが）、公式ドキュメントにおける関数の命名法なんて、公式のありがたみを感じるようなところでもありません。

ルールを決めるときに公式を参考にするのは悪いことではありませんが、自分の考えがあるならば、それと異なる、自分が納得できないルールに従うのは精神的によくありません。納得を優先しましょう[^nattoku]。

[^nattoku]: ただし、筋の悪いやり方に無理に納得してしまうと後々困りますので、納得を優先するというのは、適切な考察ができることを前提とした高度なスキルではあります。この記事のような、正直大した差を生まない話題で納得を優先していくのが良いでしょう。

## まとめ

こういう話題は正直どちらでも大差ない話ではありますが、だからこそ考えるのは楽しいし、精神衛生のためにもちゃんと自ら考えるのは価値があることです。

ぜひ自ら考えましょう。それにあたって、この記事が参考になれば幸いです。