事象
Next.js × TypeScriptを使用したアプリで、クリックしたボタンのIDと紐づいたモーダル要素を表示したかったのですが、IDを参照できずエラーとなってしまいました。
以下のコードはReact × TypeScript製で、クリックしたボタンのIDをコンソールに出力する失敗例です。
event.target.idでボタンのIDを取得しようとしましたが、うまくいきません。
import React, { MouseEvent, ReactNode } from 'react';
import './App.css';
const Button = ({
children,
id,
handleClick,
}: {
children: ReactNode;
id: string;
handleClick?: (event: MouseEvent<HTMLButtonElement>) => void;
}) => {
return (
<button onClick={handleClick} id={id}>
{children}
</button>
);
};
function App() {
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
console.log(event.target.id);
};
return (
<div className="App">
<Button handleClick={handleClick} id="button">
ボタン
</Button>
</div>
);
}
export default App;
原因
event.target
の型が確定していないため、このようなエラーが発生します。
event.targetは実際にイベントが発生した要素の情報を取得するため、TypeScriptコンパイラーから見ると何の要素なのかがわからないのです。
App関数を以下のように編集すると、わかりやすいです。
ボタンクリックで取得した要素は、button要素ではなくstrong要素になってしまいます。
function App() {
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
console.log(event.target);
console.log(event.currentTarget);
};
return (
<div className="App">
<Button handleClick={handleClick} id="button">
<strong>ボタン</strong>
</Button>
</div>
);
}
解決方法
解決方法は2つあります。
event.currentTargetを使う
event.currentTargetはイベントハンドラーを登録した要素の情報を取得します。
要素や型が特定されるため、ボタンのIDを取得することができます。
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
console.log(event.currentTarget.id);
};
instanceofで型チェックを行う
targetプロパティを使う方法もあります。
TypeScriptコンパイラーがevent.targetの型を特定できるようにinstanceof演算子で解決してあげます。
event.targetの対象がボタン要素じゃない場合は処理が終了するので、エラーを回避できます。
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
if (!(event.target instanceof HTMLButtonElement)) {
return;
}
console.log(event.target.id);
};
参考
TypeScriptでEventの取り扱いがめんどくさ過ぎる。。。
https://zenn.dev/koduki/articles/0f8fcbc9a7485b
イベントオブジェクトの target と currentTarget の違いとそれに伴う TypeScript の挙動を調べてみた
https://zenn.dev/yuyao17/articles/e0a54bd14ead9b
Event.currentTargetとEvent.targetの違いについて
https://www.javadrive.jp/javascript/event/index9.html