generated at
Reactの関数コンポーネントのイベントハンドラにデフォルト関数を設定したい
こういうソースコードがあったとする
base.tsx
/// <reference no-default-lib="true" /> /// <reference lib="esnext" /> /// <reference lib="dom" /> import React from "https://esm.sh/react@18.2.0"; import { createRoot } from "https://esm.sh/react-dom@18.2.0/client"; function ClickMe( props: { onClick?: React.MouseEventHandler<HTMLDivElement> }, ): JSX.Element { return ( <div onClick={props.onClick} style={{ width: 200, height: 200, backgroundColor: "aqua", borderRadius: 10, }} > </div> ); } const root = createRoot(document.body); root.render( <ClickMe onClick={() => alert("クリックしたよ!")}></ClickMe>, );
これは、表示される四角( <ClickMe> )をクリックするたびに「クリックしたよ!」と表示されるReactのソースコード
<ClickMe> onClick に関数を渡すことで、クリック時にメッセージを表示するようにしている
<ClickMe> onClick を書かなかった場合、通常コンポーネント内部の props.onClick の値はundefinedになる
props.onClick はコンポーネント内部に記述している <div> onClick に直接渡しているため、 <div> onClick もundefinedになる

やりたいこと
<ClickMe> onClick を書かなかった場合、別のデフォルト関数を onClick に設定するようにしたい
そんで、そのデフォルト関数の型を React.DragEventHander<HTMLDivElement> | undefined にしたい
つまり、undefinedも指定できるようにしたい
―――というのはもうできていて、Mijinko_SDはどういったアルゴリズムで書くのが良いかを迷っている

とりあえず書いてみる
function ClickMe(){} の外側の記述や、 ClickMe のreturn文の中の <div> のstyle属性は省略する
ここでは ClickMe(){} の中に追記するコードのみを記述する
パターン1
p1.tsx
const onClick(): React.MouseEventHandler<HTMLDivElement> = (e) => { if (props.onClick !== undefined) { alert("デフォルト!"); } else props.onClick(); } return(<div onClick={onClick}></div>)
p1_undefined.tsx
const onClick(): React.MouseEventHandler<HTMLDivElement> = (e) => { if (props.onClick === undefined) return; props.onClick(); } return(<div onClick={onClick}></div>)
onClick 定数を新たに作成し、その中で条件分岐をした後に props.onClick() を呼び出している
比較的短くて単純
デフォルトをundefinedにした場合にも関数オブジェクトを生成するのが欠点

パターン2
p2.tsx
let onClick: React.MouseEventHandler<HTMLDivElement> | undefined; if (props.onClick === undefined) { onClick = (e) => { alert("デフォルト!"); } } else onClick = props.onClick; return(<div onClick={onClick}></div>)
デフォルトが undefined だった場合は onClick の中身も undefined にするようにした
onClick を定数(const)にできてないので微妙

パターン3
p3.tsx
const onClick: React.MouseEventHandler<HTMLDivElement> | undefined = ( props.onClick === undefined ? { alert("デフォルト!"); } : props.onClick ) return(<div onClick={onClick}></div>)
p2.tsx を無理やり三項演算子に変換したやつ
右辺で頑張れば const を使える理論
可読性が著しく低いので論外

パターン4
p4.tsx
const onClickDefault: React.MouseEventHandler<HTMLDivElement> | undefined = { alert("デフォルト!"); } const onClick: React.MouseEventHandler<HTMLDivElement> | undefined = (props.onClick === undefined ? onClickDefault : props.onClick); return(<div onClick={onClick}></div>)
デフォルト処理を別の定数に分けることにより、三項演算子の文量を減らした
その代わり、定数が倍になってしまったけれど、これでいいものか…
型指定をいちいち書くのは普通にだるい
型ヒントにハンドラ型とundefined両方をつけているけれどツッコまないでMijinko_SD

パターン5
p5.tsx
function useDefaultHander<T>( propsHander?: T, defaultHander?: T, ): T | undefined { if (propsHander === undefined) return defaultHander; else return propsHander; } const onClick = useDefaultHander<React.MouseEventHandler<HTMLDivElement>>( props.onClick, () => { alert("デフォルト!"); }, ); return(<div onClick={onClick}></div>)
今まで三項演算子だった部分を関数に置き換えたパターン
これだけ見ると前のより長くなったように見えるが、この関数( useDefaultHander )自体は他のイベントハンドラにも使い回せることが強み
後から onMouseOver などを追加したい場合でも、この関数を使うことで追記を抑えられる
型指定も1度で済んで良い
使い回すことができると可読性も維持しやすい(多分)
const も1個に抑えることができた
useDefaultHander (デフォルトハンドラーを使用)の関数名、もっと適したのないかな?
combineDefaultHander (デフォルトハンドラーを混合)
addDefaultHander (デフォルトハンドラーを追加)

パターン6(存在しない)
defaultHanderをどっか一箇所にまとめられないかなとも考えたが、あんまり変な場所に書くと型指定を何度も書く羽目になるのでやめた

書いた後で思ったこと
そういえば、HTMLに実装されている要素のイベントにはEvent.preventDefault()ってのがあったよな
これって確か、デフォルトのイベント処理を実行させないメソッドだったよな
つまり、通常ならイベントハンドラの他に、デフォルトのイベントも処理するようになっているというわけで
でも、今回Mijinko_SDが書いたコードは、イベントハンドラが指定された時点でデフォルト動作を停止するようになっている
いちいち preventDefault() しなくていいという利点はある
Mijinko_SDの目的的には、ハンドラを指定しなかった場合の代替処理を用意できれば良いという感じなので、別に気にしなくていいか