web-dev-qa-db-ja.com

カスタムイベントの反応、ディスパッチ、リッスン

Webコンポーネントを自分の反応アプリに統合したいと思います。これらは、囲んでいるリアクションアプリがリッスンする必要がある一連のカスタムイベントを定義します。

react docs によると:

Webコンポーネントによって発行されたイベントは、React Render Treeを介して適切に伝播しない場合があります。これらのイベントをReact=コンポーネント内で処理するには、イベントハンドラーを手動でアタッチする必要があります。 。

私はこの仕事をするのに苦労しました。リアクションコンポーネントからカスタムイベントをディスパッチし、dom要素でそれらをリッスンできます。しかし、他の反応コンポーネントでこれらのイベントをキャッチすることはできません。

リアクションアプリからカスタムイベントを正しくリッスンするには(理想的には、それらをディスパッチできるようにもできます)?

最小限の例

最小限の例を設定しました( sandbox.ioでライブ編集 )。 2つのdivで構成されています。外側のdivは私のカスタムイベントをリッスンし、内側のdivは仮想domをホストします。仮想domには2つのコンポーネントがあります。外部のイベントはカスタムイベントをリッスンし、内部のイベントはそれをディスパッチします。実行すると、外側のdivがカスタムイベントを登録します。反応アプリ内では引っ掛かりません。

コードをいじりたい場合は、レポとして設定しました。

git clone https://github.com/lhk/react_custom_events
cd react_custom_events
npm i
npm run start
# browser opens, look at the console output

index.html、カスタムdom要素をリッスンするdivが含まれており、その内部がreactアプリのルートです。

<div id='listener'>
  <div id="react_root"></div>
</div>

反応アプリは、リスナーとディスパッチャーという2つの機能コンポーネントで構成されています。 index.tsx反応アプリをレンダリングし、domにリスナーをセットアップしますdiv

document.getElementById("listener")?.addEventListener("custom", ev => {
  console.log("dom received custom event");
});

ReactDOM.render(<Listener />, document.getElementById("react_root"));

そして、ここに2つのコンポーネントがあり、それらはカスタムdomイベントをディスパッチしてリッスンします。 Listener.tsx

import React, { useEffect, useRef } from "react";
import Dispatcher from "./Dispatcher";

export default function Listener() {
  const divRef = useRef(null);
  useEffect(() => {
    (divRef.current as any).addEventListener("custom", (ev:any) => {
        console.log("react received custom event");
      });
  });
  return (
    <div ref={divRef}>
      <Dispatcher />
    </div>
  );
}

Dispatcher.tsx

import React, { useEffect, useRef } from "react";
import { customEvent } from "./events";

export default function Dispatcher() {
  const pRef = useRef(null);
  useEffect(() => {
    (pRef.current as any).dispatchEvent(customEvent);
  });
  return (
    <div>
      <p ref={pRef}>Some Text</p>
    </div>
  );
}

最後に、カスタムイベントは次のように宣言されます。

export var customEvent = new Event('custom', { bubbles: true });

関連質問:

この質問はよく似ています: ReactでのWebコンポーネントイベントのリスニング

しかし、それは実際には反応のカスタムイベントのシステムについてではありません。代わりに、polymerコンポーネントのイベント属性を公開する方法を尋ねます。

この質問もカスタムイベントに関するものですが、reactのコンテキストではありません。 カスタムイベントが定義されたWebコンポーネントをリッスンする方法

この質問は構文エラーのようです: addEventListener in a React app is n't working

3
lhk

これは単に反応することは不可能であるように私には思えます。

Reactは合成イベントのシステムを提供します。これは互換性の理由で実装されています。合成イベントは、ブラウザー間で同じAPIを公開します。内部的に、reactはネイティブDOMイベントを合成イベントにラップします。

ただし、これらの合成イベントには一連の制限もあります。

  • 合成イベントは、すべてのネイティブイベントをカバーするわけではありません。最も注目すべきは、onInputとonChangeの周りに変更があることです。
  • カスタムイベントの合成バージョンはありません
  • ネイティブ(カスタム)イベントは、イベントリスナーをトリガーすることなく、仮想domをバブルします。仮想domを離れた後、彼らはdomの残りの部分を泡立たせ続けます。これが私のコード例で起こっていることです。
  • 自分で合成イベントをディスパッチすることはできません

仮想domでカスタムイベントをリッスンすることは可能ですが、それらをディスパッチする同じ要素でのみです。次のコードはイベントをキャッチします。

export default function Dispatcher() {
  const pRef = useRef(null);
  useEffect(() => {
    (pRef.current as any).addEventListener('custom', ()=>{
      console.log('react caught custom event directly on component');
    });
    (pRef.current as any).dispatchEvent(customEvent);
  });
  return (
    <div>
      <p ref={pRef}>Some Text</p>
    </div>
  );
}

これはカスタムイベントをほとんど役に立たないと主張します。カスタムイベントをトリガーした特定のdomノードへの参照を取得する必要がある場合は、それにコールバックを与えることもできます。

これについて読むことに興味があるなら、さまざまなgithubの問題があります。残念ながら、facebook反応チームはそれらをかなり重く管理しているようです。彼らはそれ以上コメントなしで単に閉じられます:(

0
lhk