Portal 中的事件冒泡机制

React Portal 渲染到其他 DOM 节点后,事件是否还能冒泡到父组件

问题

子组件使用 Portal 渲染到其他 DOM 节点后,点击事件能否冒泡到父组件?

解答

答案是可以的。 Portal 虽然在 DOM 结构上渲染到其他位置,但在 React 组件树中仍然保持父子关系,事件会沿着组件树冒泡。

工作原理

React Portal 的事件冒泡依赖三个机制:

1. React Context 机制

Portal 在 React 内部仍然保持在父组件树中,即使 DOM 渲染到其他位置。Portal 的 context 依然从父组件继承。

2. DOM 事件冒泡

DOM 事件从触发元素开始,沿 DOM 树向上冒泡到 document。但这不影响 React 的事件处理。

3. React 事件代理

React 使用事件代理模式,将所有事件代理到顶层(document 或 root 节点)统一处理。事件会沿着 React 组件树(而非 DOM 树)冒泡。

示例代码

function Parent() {
  const handleClick = () => {
    console.log('父组件捕获到点击事件');
  };

  return (
    <div onClick={handleClick}>
      <h1>父组件</h1>
      <ChildWithPortal />
    </div>
  );
}

function ChildWithPortal() {
  return ReactDOM.createPortal(
    <button>点击我</button>,
    document.body // 渲染到 body,而非父组件 div 内
  );
}

// 点击按钮时,父组件的 handleClick 会被触发

关键点

  • Portal 在 DOM 结构上渲染到其他位置,但在 React 组件树中仍是父组件的子节点
  • React 事件沿组件树冒泡,而非 DOM 树
  • React 使用事件代理机制,在顶层统一处理所有事件
  • 父组件可以正常捕获 Portal 子组件触发的事件