【1日1zenn - day2】React + TypeScriptでonClickに出るエラー
※1日1zenn用のアウトプットなので分厚くないです。
onClickで、とある関数を実行するよう定義したところ以下のエラーが出ました。
基礎基礎の基礎だと思いますが、備忘。
関数の呼び出し方はあんまり意識してなかったのでいい機会。
エラー内容
エラー文言
型'hogehoge'を型 'MouseEventHandler<HTMLDivElement> | undefined'に割り当てることはできません。型'hogehoge'にはシグネチャ'(event: MouseEvent<HTMLDivElement, MouseEvent>): void'に一致するものがありません。ts(2322)
再現しようとしたら実はちょっと違うエラー文言になってたのですが、大体上記みたいな感じだった気がします。
なんか雰囲気的にクリックとかのイベントを直接使うような関数を指定しないとダメっぽいってコト??と混乱した記憶。
エラーを起こしていた実装
かなり抽象化してるので「そんな実装せんやろ」となってますが。
// 定義
const handleClick = useHandleClickIcon()
// 実行
<div onClick={handleClick}>
export const useHandleClickIcon = () => {
// なんか処理
return (
userId,
userName,
)
}
要因と対策めも
要因
要因の一つは以下の部分。
const handleClick = useHandleClickIcon()
上記の書き方だと、handleClickの中身には関数ではなく、useHandleClickIcon()
の実行結果が入ってしまっています。
onClick={handleClick}
はクリックされた時にhandleClickという関数を実行しようとするので、今回のように「onClickは関数を期待するのに値が入ってしまってますよ」というエラーが起こります。
後述しますが、useHandleClickIcon()
の実行結果が関数ならOKです。
対策
その1
handleClickの実装を以下のようにする
// 定義
const handleClick = () => {
useHandleClickIcon()
}
// 実行
<div onClick={handleClick}>
handleClickをアロー関数にすることで、handleClickはuseHandleClickIcon()
の返り値ではなくuseHandleClickIcon()
を実行する関数になるため型エラーが起きなくなる。
その2
onClickの定義を以下のようにする
// 定義(そのまま)
const handleClick = useHandleClickIcon()
// 実行(アロー関数にラップ)
<div onClick={() => handleClick()}>
こういう書き方をするのは、特にhandleClickに引数が必要な時です。
引数を与えるために
ちなみに、
onClick={handleClick(userId)}
という書き方にすると、ちゃんと関数を定義しているので表題のエラーは出なくなるけど、関数を()付きで呼び出してしまっているのでレンダリングされた時点で関数を実行してしまいます。すると無限に再レンダリングが走るようになり、エラーになります。
よって引数を与える時は<div onClick={() => handleClick()}>
みたいにアロー関数でラップすることで、クリックされた時に実行する関数として定義しましょう。
その3
また今回は結局、useHandleClickIcon()
をuseCallbackに変えることにしたので上記は関係なくなりました。
// 定義
const handleClick = useHandleClickIcon()
// 実行
<div onClick={handleClick}>
export const useHandleClickIcon = () => {
// なんか処理
return useCallback(()=>{
handleHogeHoge
})
}
useCallbackのドキュメントにも以下のように書いてありますが、useCallbackは関数を返すので、onClickは大歓迎してくれる感じですね。
React は初回のレンダー時にはその関数をそのまま返します(呼び出しません!)。次回以降のレンダーでは、ひとつ前のレンダー時から dependencies が変更されていない場合、React は再び同じ関数を返します。それ以外の場合は、今回のレンダー時に渡された関数を返しつつ、後で再利用できる場合に備えて保存します。React は関数を呼び出しません。関数自体が返されるので、呼ぶか呼ばないか、いつ呼ぶのかについてはあなたが決定できます。
初回のレンダー時、useCallback は渡された fn 関数を返します。
その後のレンダー時には、前回のレンダーからすでに保存されている fn 関数を返すか(依存配列が変更されていない場合)、このレンダー時に渡された fn 関数を返します。
Discussion