DOM イベント ハンドラーを登録およびクリアするためのより簡単な方法


最初に公開された場所: https://farzadyz.com/blog/a-simpler-approach-to-registering-and-clearing-dom-event-handlers

あなたが私に尋ねると、DOMイベントハンドラーは奇妙なインターフェースで動作します.あなたがする必要があるという事実
イベント ハンドラーへの参照を保持してクリアできるようにすることは、特に
複数のイベント ハンドラを処理しようとしています.コマンド パレットまたはキーボード ショートカットをアプリケーションに組み込み、それを行う必要があると想像してください.
大量のハンドラー変数への参照を保持します.これは制御不能な成長のレシピです.もちろん、キー値を保持できます
イベントのペアをそれぞれのハンドラーに渡しますが、それはブラウザーの内部を再発明するような気がします.

イベント ハンドラーをクリアすると、イベントが改善されます.まったく同じ引数を渡す必要がありますが、今回はハンドラーをクリーンアップするために removeEventListener に渡します.次の例を見てください.

const clickHandler = () => {
  console.log("clicked");
};
element.addEventListener("click", clickHandler);
// You MUST pass the same reference to the handler because the event registry saves them by reference
// If you lose the reference or pass the handler function directly to `addEventListener`, there would be no way to clear it
element.removeEventListener("click", clickHandler);


特にサブスクリプションが通常より大きなコードの一部であることを考えると、後でコード内でクリアできるようにするためだけにハンドラー関数への参照を保持しなければならないのは面倒なプロセスになる可能性があります.これは、あまりにも多くの変数を宣言したり、より大きなオブジェクトをスパムしたりするための道です.

しかし、どうすればこれを簡単にできるでしょうか?



サブスクリプションのクリアランスを簡単にする一般的なパターンは、一度呼び出されるとサブスクリプションを自動的にクリアする関数を返すことです.これは、多くのライブラリで使用されるよく知られたパターンです.これは、React の useEffect で既に見ました.useEffect では、エフェクト内のサブスクリプションをクリアするための関数を返す必要があります.またはどのように XState expects you to return a clearance function from invocations .

クリアを簡単にするために、同じパターンに従う小さな便利な関数を書くことができます.

DOM イベントリスナーから始めましょう.

// ...args: [event, handler, capture]
function onEvent(element, ...args) {
  element.addEventListener(...args);
  return () => {
    element.removeEventListener(...args);
  };
}


上記のコードを使用する方法は次のとおりです.

<form>
  <div>
    <label for="name">Name</label>
    <input id="name" name="name" />
  </div>
  <button>Submit</button>
</form>

<script>
  const $form = document.querySelector("form");
  const onSubmit = (e) => {
    // post to server
  };
  const clearSubmit = onEvent($form, "submit", submitForm);

  // When needed to clear it
  clearSubmit();
  // as apposed to $form.removeEventListener('form', submitForm)
</script>


タイプセーフにする



上記のユーティリティのタイプ セーフ バージョンを使用するには、Typescript の DOM タイプからほとんどのタイピングを借用できます.

function onEvent<E extends HTMLElement>(
  element: E,
  ...args: Parameters<HTMLElement["addEventListener"]>
) {
  element.addEventListener(...args);
  return () => {
    element.removeEventListener(...args);
  };
}


ジェネリック型を使用して要素の型を柔軟に保ちますが、それがどの要素になるかはわかりませんが、 HTMLELement を拡張する要素に制限します.

残りの引数をタイプセーフにするために、引数を渡すだけなので、基本的に element.addEventListener から定義を取得できます.

これはどのように役立ちますか?



まず、ハンドラーへの参照を保持するためのコードを数行節約できます.
第 2 に、それがどのイベントであったか、どの要素にアタッチされているか、イベントがどのように登録されたか (その他の引数) を知る必要がなくなりました.関心があるのは、ハングしているサブスクリプションをクリアする関数を呼び出すことだけです.