6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【React】addEventListenerを追加した後にremoveEventListener(削除)する方法

Last updated at Posted at 2022-06-20

はじめに

Reactでイベントリスナの追加と削除について書いていきます。
  • イベントリスナの追加はできたけど、削除はできない。
  • ページ遷移時のイベントリスナの削除の仕方がわからない。
上記の方の参考になればと思います。

addEventListenerについて

addEventListener()メソッドは、イベントリスナを登録するの使用されます。
Reactでの使い所としましては、
・ブラウザバックの制御
・PCの再リロード制御
・ブラウザを閉じる時の制御
・APIによってはイベントリスナでしか対応してないもの
上記かなっと思います。(まだまだありそうですが...)

ハンドラを追加する構文は次のようになります。

addEventListener.js
element.addEventListener(event, handler[, phase]);

まず最初のelement部分はEventTargetと言われ、イベントを設定する所を
指定しています。よく指定されるのが、
・Window
・document
・element( DOMにidとかつけてdocument.getElementById("hoge")とかで取得してくるものとか )
が多いですね。他にもありますので気になる方は次のドキュメントで確認ください。
参考:EventTargetリファレンスサイト

次に引数部分ですが
event
イベント名です。 click keydown ...etc
参考:JavaScriptのイベントハンドラ一覧
handler
ハンドラ関数.
phase
オプションの引数で、ハンドラが動作する “フェーズ” です。基本は使いません。ただ処理速度とか考えるなら知ってるほうがいいです。(以下参考記事)
参考:addEventListener の第3引数が拡張されてるという話

これらを使用してイベントリスナを登録していきます。

イベントリスナ
addEventListener メソッドを使用してイベントに対して登録した関数のこと

ハンドラ
イベント発生時に実行する関数のこと

次に登録後、不要になったので削除する場合ですね。

removeEventListener

削除するにはremoveEventListener()メソッドを使います。
構文は次になります。

removeEventListener.js
// addEventListener とまったく同じ引数にする必要があります。
element.removeEventListener(event, handler[, phase]);

削除する時はaddEventListenerとまったく同じ引数にしないと登録したイベントリスナは削除されません。

例:削除されないパターン

eventListener.js
// 追加
document.addEventListener( "click" , () => alert('hoge'));
// 削除
document.removeEventListener( "click", () => alert('hoge'));

イベントリスナハンドラは削除されません。removeEventListener は別の関数を取得しているためです。同じコードでも関係ありません。

例:削除されるパターン

eventListener.js
// ハンドラ
const handler = () => {
  alert( 'hoge' );
}
// 作成
document.addEventListener("click", handler);
// 削除
document.removeEventListener("click", handler);

変数に関数を所持することで削除が可能になります。逆に言いますと変数で関数を所持しないと削除はできなくなります。

参考:現代のJavaScript : addEventListener

ドキュメントや他webサイトに上記の内容が書いてあるんですけど、ただReactで素直に設定しようとすると落とし穴にはまってしまうんですね。(自分ははまりました(笑))

Reactでのイベントリスナの設定

素直にイベントリスナを設定した場合(失敗例)

イベントリスナ設定のサンプルコードを作っていきます。 今回はドキュメント押下したらカウントアップするハンドラを使用します。

app.jsx
import { useState } from 'react';

export default function App(props) {
  // イベントリスナ追加されたか確認用
  const [count, setCount] = useState(0);

 // カウントをアップする(ハンドラ)
  const countUp = ()=>{
    setCount(count => {
      count++;
      return count;
    })
  };

  // ボタン押下時の処理
  const SetEvent = (bMode) => {
    // true:イベントリスナー追加 false:イベントリスナー削除
    if (bMode) {
      document.addEventListener('click', countUp);
    } else {
      document.removeEventListener('click', countUp);
    }
  };

  return (
    <div>
      <button
        onClick={() => {
          SetEvent(true);
        }}
      >
        addEventListener
      </button>
      <button
        onClick={() => {
          SetEvent(false);
        }}
      >
        removeEventListener
      </button>
      <div>
        <h1>count:{count}</h1>
      </div>
    </div>
  );
}

画面がこちら
addevent.png

addEventListener ボタンを押下したらイベントリスナが登録され、removeEventListenerで削除できるように作ったつもりです。
削除する時に気をつける、引数一緒も意識しました。(削除はされません)

実際の動き(画像で申し訳ないです。)
image.png

addEventListenerのボタンを押下する度にイベントリスナが追加されています。
removeEventListenerのボタンでイベントリスナが削除されていません。
引数は同じなのに削除できないのが不思議です。

Reactの性質について考えて作り直したもの(成功例)

Reactは状態が変化すると再レンダリングします。レンダリング処理が走る度にコンポーネント内の関数が新規で作成されているのです。
という事は同じ変数名の関数でも厳密に見ると違う関数になるという事です。
レンダリングの度に違う関数....、state使ってるから外にも書けないし....。
となりましたが、reactに優秀なhooksがいます。関数をメモ化すれば新規で作られる事はありません。

useCallback

今回はこのhooksを使用して関数をメモ化していきます。

参考:React hooksを基礎から理解する (useCallback編+ React.memo)

作り直したサンプルコード

app.jsx
import { useState, useCallback } from 'react';

export default function App(props) {
  // イベントリスナ追加されたか確認用
  const [count, setCount] = useState(0);

 // カウントをアップする(ハンドラ)
  const countUp = useCallback(()=>{
    setCount(count => {
      count++;
      return count;
    })
  },[]);

  // ボタン押下時の処理
  const SetEvent = (bMode) => {
    // true:イベントリスナー追加 false:イベントリスナー削除
    if (bMode) {
      document.addEventListener('click', countUp);
    } else {
      document.removeEventListener('click', countUp);
    }
  };

  return (
    <div>
      <button
        onClick={() => {
          SetEvent(true);
        }}
      >
        addEventListener
      </button>
      <button
        onClick={() => {
          SetEvent(false);
        }}
      >
        removeEventListener
      </button>
      <div>
        <h1>count:{count}</h1>
      </div>
    </div>
  );
}

イベントリスナに登録する関数(カウントアップ関数)を useCallback でラップしました。これで関数がメモ化されたのでイベントリスナの積み重ねがなくなり、削除もできるようになりました。

今回は説明はしませんが、メモ化されたハンドラないでif文を使い、分岐の判定を useStateでしたい場合は、かわりにuseRefを使うとしっかりと分岐の判定をしてくれます。

ポイント
Reactでイベントリスナーを設定する場合は関数(ハンドラ)をメモ化しましょう。

Reactでページ遷移するタイミグで解除する方法

遷移する時はuseEffectを使用すると遷移するタイミングで解除できます。

app.jsx

 // カウントをアップする(ハンドラ)
  const countUp = useCallback(()=>{
    setCount(count => {
      count++;
      return count;
    })
  },[]);

  // イベントリスナーの設定
  useEffect(() => {
    // イベントの設定
    document.addEventListener('click', countUp);
   
    return () => {
      // イベントの設定解除
      document.removeEventListener('click', countUp);
    };
  }, []);

上のコードの例ですとレンダリングされた時にイベントリスナを設定して、ページ遷移するタイミングでイベントリスナを削除します。
こんな形で遷移時に追加したいイベントを書きます。

終わり

説明不足もあると思いますが、イベントリスナの設定を自分なりにまとめられてよかったです。

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?