今日の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} />
筆者であれば、さすがにこれはやりません。直接addUser
をonClick
に渡します。
// さすがにこうする
<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。
逆にいえば、onClick
やonSubmit
といったイベント名が直接現れる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。
まとめ
こういう話題は正直どちらでも大差ない話ではありますが、だからこそ考えるのは楽しいし、精神衛生のためにもちゃんと自ら考えるのは価値があることです。
ぜひ自ら考えましょう。それにあたって、この記事が参考になれば幸いです。
-
余談ですが、「すべからく」と書くと、誤用ではなくても誤用警察が現れるのが日常茶飯事です。これは本来の意味での使い方のつもりですが、果たして誤用警察は現れるでしょうか。 ↩
-
AddUserPage
ではなくAddUserForm
みたいな粒度のコンポーネントであれば、submitはイベント名ではなくただの動詞だよということでonSubmit
でもいいかもしれません。今回はPage
という粒度のコンポーネントにしたので、一般の動詞としてもonSubmit
は不自然でしょう。 ↩ -
ただし、筋の悪いやり方に無理に納得してしまうと後々困りますので、納得を優先するというのは、適切な考察ができることを前提とした高度なスキルではあります。この記事のような、正直大した差を生まない話題で納得を優先していくのが良いでしょう。 ↩