Portal 子组件的事件冒泡

React Portal 渲染到其他 DOM 节点后,事件如何冒泡到父组件

问题

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

解答

可以。Portal 虽然在 DOM 结构上渲染到其他位置,但在 React 组件树中仍然是父组件的子节点,事件会沿着组件树(而非 DOM 树)冒泡。

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

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

function PortalChild() {
  return ReactDOM.createPortal(
    <button>点击我</button>,
    document.getElementById('portal-root')
  );
}

在上面的例子中,点击按钮会触发父组件的 handleClick,即使按钮实际渲染在 #portal-root 节点下。

这是因为 React 使用事件代理机制,将所有事件代理到根节点统一处理。当事件触发时,React 会沿着组件树路径冒泡,而不是 DOM 树路径。Portal 组件在 React 组件树中的位置没有改变,所以事件冒泡行为也不会改变。

关键点

  • Portal 改变的是 DOM 渲染位置,不改变 React 组件树结构
  • React 事件沿着组件树冒泡,而非 DOM 树
  • React 使用事件代理,在根节点统一处理事件
  • Portal 的 context 仍然从父组件继承