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