6
3

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 3 years have passed since last update.

ReactでpreventDefaultできない問題に対処する

Last updated at Posted at 2021-06-17

はじめに

TouchEventなどに対するイベントリスナでは、Event.preventDefault() をよびだすことができません。もしイベントリスナ内で呼び出そうとすると、次のようなエラーを目にするでしょう。

[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See...

これは、イベントリスナが受動イベントリスナとして扱われていることが原因です。Event.preventDefault() をよびだすには、イベントリスナが受動的でないことが必要なので、本記事ではReactでイベントリスナを受動的でない形で定義する方法を説明します。

非受動イベントリスナを設定する

(以下、TypeScriptで記述しますがJavaScriptでも同様です。)

次のような、内部にSVGの<Rect> エレメントを持つReactコンポーネントを考えます。

const MyCircle = () => {
  return(
    <rect x={0} y={10} width={50} height={100} /> 
  );
};

ここにタッチイベントに対するリスナ(長方形を触るとイベントが発火する)を定義するには、次のようにします。

const MyCircle = () => {
  // タッチイベントリスナ
  const onTouchStart = (event: Event) => {
    e.preventDefault(); // ここでpreventDefault()を呼べる
    const touchEvent = event as TouchEvent;
    console.log(touchEvent.changedTouches[0].pageX);
  };
  const onTouchMove = (event: Event) => {...}; // onTouchStartと同様
  const onTouchEnd = (event: Event) => {...}; // onTouchStartと同様

  const circleRef = useRef<SVGRectElement>(null);
  useEffect(() => {
    ref.current?.addEventListener("touchstart", onTouchStart, { passive: false });
    ref.current?.addEventListener("touchmove", onTouchMove, { passive: false });
    ref.current?.addEventListener("touchend", onTouchEnd, { passive: false });
    return (() => {
      ref.current?.removeEventListener("touchstart", onTouchStart);
      ref.current?.removeEventListener("touchmove", onTouchMove);
      ref.current?.removeEventListener("touchend", onTouchEnd);
    });
  });
  return(
    <rect x={0} y={10} width={50} height={100} ref={circleRef} /> 
  );
};

ポイントは次のとおりです。

  • イベントリスナの引数はEvent 型とする。TouchEvent型のプロパティを利用するときは、キャストする。
  • DOM要素には、refを設定してアクセスする
  • useEffect内で、 ref.current?.addEventListener() でリスナを設定する。オプションとして{ passive: false } を渡す。
  • クリーンアップ時に ref.current?.removeEventListener() でリスナを除去する。

これで、TouchEventのリスナでもEvent.preventDefault() が動作するようになります。

おわりに

こんな面倒なことしなくても非受動イベントリスナを設定できるようになるといいですねorz

6
3
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
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?