イベントリスナー内のthisの参照問題
TypeScriptでクラスメソッドをイベントリスナーとして使用する際に、thisの参照が期待通りにならない問題が発生することがあります。
これを解決する方法として、bindメソッドを使用するという方法があります。
今回は、この問題の発生原因とbindを使った解決法について、実際のコードを元に紹介します!
1. 問題の発生
イベントリスナー内でクラスメソッドを呼び出すと、thisの参照が予期せず上書きされ、意図しない挙動を引き起こすことがあります。
例えば、以下のコードように、生成したボタンへ押下時に「Click me!」を表示させるメソッドをイベントリスナーにて設定しようとしたときに、問題が発生します。
class MyButton {
private buttonText: string;
constructor(buttonText: string) {
this.buttonText = buttonText;
this.setupButton();
}
private setupButton() {
const button = document.createElement('button');
button.innerText = this.buttonText;
button.addEventListener('click', this.handleClick);
document.body.appendChild(button);
}
private handleClick() {
// 'this'の参照が 'MyButton' インスタンスではなく、
// イベントが発生した 'button' 要素になってしまう
console.log(this.buttonText);
}
}
const myButton = new MyButton('Click me!');
ボタンを押下した時、イベントリスナーにて、handleClickメソッドが呼び出されるのですが、その際、this.buttonTextにアクセスします。
しかし、実際でのthisの参照がbutton要素になってしまい、button要素には勿論buttonTextプロパティなどなくundefinedとなってしまい、想定と異なる挙動になってしまいます!
2. 解決
この問題を解決するために、bindメソッドを使ってイベントリスナー内でのthisの参照を正しく設定できます!
class MyButton {
// ...
private setupButton() {
const button = document.createElement('button');
button.innerText = this.buttonText;
// bindメソッドを使って、handleClickメソッド内の'this'を'MyButton'インスタンスに固定する
button.addEventListener('click', this.handleClick.bind(this));
document.body.appendChild(button);
}
private handleClick() {
// 'this'は'MyButton'インスタンスを指す
console.log(this.buttonText);
}
}
このようにbindメソッドを使用することで、handleClickメソッド内でのthisの参照を正しく設定できます!
まとめ
thisの参照の問題はよく発生してしまうので、どこを参照してしまうのかを意識してコードを書いていきましょう!
次回は、このbindメソッドをもっと再利用できるように、デコレータを使ってのbindメソッドの使用例を紹介します!