Reactの関数コンポーネントのイベントハンドラにデフォルト関数を設定したい
こういうソースコードがあったとする
code:base.tsx
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
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にしたい
―――というのはもうできていて、Mijinko_SD.iconはどういったアルゴリズムで書くのが良いかを迷っている とりあえず書いてみる
function ClickMe(){}の外側の記述や、ClickMeのreturn文の中の<div>のstyle属性は省略する
ここではClickMe(){}の中に追記するコードのみを記述する
パターン1
code:p1.tsx
const onClick(): React.MouseEventHandler<HTMLDivElement> = (e) => {
if (props.onClick !== undefined) {
alert("デフォルト!");
} else props.onClick();
}
return(<div onClick={onClick}></div>)
code:p1_undefined.tsx
const onClick(): React.MouseEventHandler<HTMLDivElement> = (e) => {
if (props.onClick === undefined) return;
props.onClick();
}
return(<div onClick={onClick}></div>)
onClick定数を新たに作成し、その中で条件分岐をした後にprops.onClick()を呼び出している
比較的短くて単純
パターン2
code: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
code:p3.tsx
const onClick: React.MouseEventHandler<HTMLDivElement> | undefined = (
props.onClick === undefined
? {
alert("デフォルト!");
} : props.onClick
)
return(<div onClick={onClick}></div>)
右辺で頑張ればconstを使える理論
可読性が著しく低いので論外
パターン4
code: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.icon
パターン5
code: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をどっか一箇所にまとめられないかなとも考えたが、あんまり変な場所に書くと型指定を何度も書く羽目になるのでやめた
書いた後で思ったこと
これって確か、デフォルトのイベント処理を実行させないメソッドだったよな
つまり、通常ならイベントハンドラの他に、デフォルトのイベントも処理するようになっているというわけで
でも、今回Mijinko_SD.iconが書いたコードは、イベントハンドラが指定された時点でデフォルト動作を停止するようになっている
いちいちpreventDefault()しなくていいという利点はある
Mijinko_SD.iconの目的的には、ハンドラを指定しなかった場合の代替処理を用意できれば良いという感じなので、別に気にしなくていいか