react:调度和监听自定义事件

lhk*_*lhk 8 javascript web-component dom-events reactjs

我想将 Web 组件集成到我的 React 应用程序中。它们定义了一组封闭的 React 应用程序必须监听的自定义事件。

根据反应文档

Web 组件发出的事件可能无法通过 React 渲染树正确传播。您需要手动附加事件处理程序来处理 React 组件中的这些事件。

我一直在努力完成这项工作。我可以从 react 组件分派自定义事件并在 dom 元素上监听它们。但是我无法在其他 React 组件上捕获这些事件。

我如何从 React 应用程序中正确地监听自定义事件(理想情况下,还能够发送它们)?

最小的例子

我已经设置了一个最小的示例(在 sandbox.io 上实时编辑)。它由两个 div 组成,外面的一个监听我的自定义事件,里面的一个托管虚拟 dom。在虚拟 dom 中有两个组件。外层监听自定义事件,内层调度它。运行时,外部 div 注册自定义事件。在反应应用程序内部,它没有被捕获。

如果您想使用代码,我已将其设置为 repo:

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
Run Code Online (Sandbox Code Playgroud)

index.html, 包含一个 div ,它将监听自定义 dom 元素,其中包含 react 应用程序的根。

<div id='listener'>
  <div id="react_root"></div>
</div>
Run Code Online (Sandbox Code Playgroud)

React 应用程序由两个功能组件组成:侦听器和调度器。index.tsx呈现 react 应用程序并在 dom 上设置一个侦听器div

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

ReactDOM.render(<Listener />, document.getElementById("react_root"));
Run Code Online (Sandbox Code Playgroud)

这是两个组件,它们分派和侦听自定义 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>
  );
}
Run Code Online (Sandbox Code Playgroud)

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>
  );
}
Run Code Online (Sandbox Code Playgroud)

最后,自定义事件声明如下:

export var customEvent = new Event('custom', { bubbles: true });
Run Code Online (Sandbox Code Playgroud)

相关问题:

这个问题听起来很相似:Listing for web component events in React

但这并不是真正关于反应中的自定义事件系统。相反,它询问如何在聚合物组件上公开事件属性。

这个问题也是关于自定义事件的,但不是在 react 的上下文中: How to listen for custom events defined web component

这个问题似乎只是一个语法错误:React 应用程序中的 addEventListener 不起作用

lhk*_*lhk 5

在我看来,这在反应中根本不可能。

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>
  );
}
Run Code Online (Sandbox Code Playgroud)

我认为这使得自定义事件几乎毫无用处。如果您必须获得对触发自定义事件的特定 dom 节点的引用,您不妨给它一个回调。

如果您有兴趣阅读此内容,可以查看各种 github 问题。不幸的是,facebook 反应团队似乎对它们进行了相当大的缓和。他们只是关闭而没有进一步评论:(