129
56

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-01-26

今日のXでは、イベントハンドラの関数名についての話題を見ました。元の投稿はこちらです。要するに、次のhandleClickのようなhandleイベント名という命名は良くないということです。

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

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

この記事の目的

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

筆者の考え

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

特に、イベントオブジェクトを取り扱う必要がある場合のことを考えてみましょう。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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を例に用います)。

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

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

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

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

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

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

React?

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

実際、Vueの公式ガイドReactの公式ガイドを見比べると、ReactではhandleClickのような命名をしており、Vueはしていません。(Vueの方はfunction greet(event)のような命名になっており、前述のとおりそれはどうなのとちょっと思いますが)。文化的な差も無いわけではなさそうです。

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

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

まとめ

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

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

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

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

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

129
56
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
129
56

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?