ReactJS SyntheticEvent stopPropagation() 仅适用于 React 事件?

Posted

技术标签:

【中文标题】ReactJS SyntheticEvent stopPropagation() 仅适用于 React 事件?【英文标题】:ReactJS SyntheticEvent stopPropagation() only works with React events? 【发布时间】:2014-08-16 10:15:26 【问题描述】:

我正在尝试在 ReactJS 组件中使用 event.stopPropagation() 来阻止单击事件冒泡并触发在遗留代码中附加到 JQuery 的单击事件,但似乎 React 的 stopPropagation() 仅停止传播到也附加在 React 中的事件,并且 JQuery 的 stopPropagation() 不会停止传播到 React 附加的事件。

有什么方法可以让 stopPropagation() 在这些事件中工作?我写了一个简单的JSFiddle 来演示这些行为:

/** @jsx React.DOM */
var Propagation = React.createClass(
    alert: function()
        alert('React Alert');
    ,
    stopPropagation: function(e)
        e.stopPropagation();
    ,
    render: function()
        return (
            <div>
                <div onClick=this.alert>
                    <a href="#" onClick=this.stopPropagation>React Stop Propagation on React Event</a>
                </div>
                <div className="alert">
                    <a href="#" onClick=this.stopPropagation>React Stop Propagation on JQuery Event</a>
                </div>
                <div onClick=this.alert>
                    <a href="#" className="stop-propagation">JQuery Stop Propagation on React Event</a>
                </div>
                <div className="alert">
                    <a href="#" className="stop-propagation">JQuery Stop Propagation on JQuery Event</a>
                </div>
            </div>
        );
    
);

React.renderComponent(<Propagation />, document.body);

$(function()    
    $(document).on('click', '.alert', function(e)
        alert('Jquery Alert');
    );

    $(document).on('click', '.stop-propagation', function(e)
        e.stopPropagation();
    );
);

【问题讨论】:

React 的实际事件监听器也在文档的根目录,这意味着点击事件已经冒泡到根目录。您可以使用event.nativeEvent.stopImmediatePropagation 来阻止其他事件侦听器触发,但不保证执行顺序。 其实stopImmediatePropagation声称事件监听器会按照绑定的顺序被调用。如果你的 React JS 在你的 jQuery 之前被初始化(就像它在你的小提琴中一样),停止立即传播将起作用。 React 阻止调用 jQuery 侦听器:jsfiddle.net/7LEDT/5 jQuery 无法阻止调用 React,因为 jQuery 侦听器稍后会在您的小提琴中绑定。 一种选择是在 componentDidMount 中设置您自己的 jQuery 事件处理程序,但它可能会以意想不到的方式干扰其他 React 事件处理程序。 我意识到.stop-propagation 上的第二个示例一定行不通。您的示例使用事件委托,但试图在元素处停止传播。侦听器需要绑定到元素本身:$('.stop-propagation').on('click', function(e) e.stopPropagation(); );。这个小提琴可以防止像您尝试的所有传播:jsfiddle.net/7LEDT/6 【参考方案1】:

React 在document 上使用带有单个事件侦听器的事件委托来处理冒泡事件,例如本例中的“点击”,这意味着无法停止传播;当你在 React 中与它交互时,真实的事件已经传播了。 stopPropagation React 的合成事件是可能的,因为 React 在内部处理合成事件的传播。

使用JSFiddle 进行下面的修复。

在 jQuery 事件上反应停止传播

使用Event.stopImmediatePropagation 防止调用根上的其他(在本例中为jQuery)侦听器。它在 IE9+ 和现代浏览器中受支持。

stopPropagation: function(e)
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
,
警告:侦听器按绑定顺序调用。必须在其他代码(此处为 jQuery)之前初始化 React 才能使其正常工作。

jQuery 停止 React 事件的传播

您的 jQuery 代码也使用事件委托,这意味着在处理程序中调用 stopPropagation 不会停止任何事情;事件已经传播到document,React 的监听器会被触发。

// Listener bound to `document`, event delegation
$(document).on('click', '.stop-propagation', function(e)
    e.stopPropagation();
);

为了防止传播到元素之外,监听器必须绑定到元素本身:

// Listener bound to `.stop-propagation`, no delegation
$('.stop-propagation').on('click', function(e)
    e.stopPropagation();
);

编辑(2016 年 1 月 14 日): 澄清委派必须仅用于冒泡事件。有关事件处理的更多详细信息,React 的源有描述性 cmets:ReactBrowserEventEmitter.js。

【讨论】:

@ssoellen:澄清:React 是否将其事件侦听器绑定在 document 或根 React 组件上? linked page 只是说“在顶层”,我认为它不是 document(但我不知道这是否相关)。 @FighterJet 这取决于事件类型。对于冒泡的事件,使用***委托。对于不冒泡的事件,React 必须监听各种 DOM 节点。 ReactBrowserEventEmitter.js 中包含更详尽的描述:github.com/facebook/react/blob/… 我对临时投反对票表示歉意。我需要它来报告错误,我已经用赞成票代替了它:) 还可以查看 Jim 的回答,它建议将全局点击侦听器添加到 window 而不是 document:***.com/a/52879137/438273【参考方案2】:

这仍然是一个有趣的时刻:

ev.preventDefault()
ev.stopPropagation();
ev.nativeEvent.stopImmediatePropagation();

如果你的函数被标签包裹,请使用这个结构

【讨论】:

event.nativeEvent 是正确答案;我可以通过它使用composedPath()event.nativeEvent.composedPath() wrapped by tag 是什么意思?你能举个例子吗? @JimmyLv 我的意思是&lt;div onClick=(firstHandler)&gt; &lt;div onClick=(secHandler)&gt;active text&lt;/div&gt; &lt;/div&gt;【参考方案3】:

值得注意(来自this issue),如果您将事件附加到documente.stopPropagation() 将无济于事。作为一种解决方法,您可以使用window.addEventListener() 代替document.addEventListener,然后event.stopPropagation() 将阻止事件传播到窗口。

【讨论】:

非常感谢!这正是我所拥有的,并且此解决方案有效。【参考方案4】:

我能够通过将以下内容添加到我的组件来解决此问题:

componentDidMount() 
  ReactDOM.findDOMNode(this).addEventListener('click', (event) => 
    event.stopPropagation();
  , false);

【讨论】:

这将完全停止 SynteticEvents,因为 React 使用事件委托和文档上的单个事件侦听器来处理冒泡事件。【参考方案5】:

来自React documentation: 下面的事件处理程序由冒泡阶段的事件触发。要为捕获阶段注册事件处理程序,请追加捕获。 (强调)

如果您的 React 代码中有一个单击事件侦听器并且您不希望它冒泡,我认为您想要做的是使用 onClickCapture 而不是 onClick。然后您将事件传递给处理程序并执行event.nativeEvent.stopPropagation() 以防止本机事件冒泡到普通 JS 事件侦听器(或任何没有反应的东西)。

【讨论】:

谢谢,截至 2021 年和 React 17,这对我有用。 onClickCapture=e => e.stopPropagation() 就足够了,不必使用 event.nativeEvent 来停止传播到根。【参考方案6】:

React 17 将事件委托给 root 而不是 document,这可能会解决问题。 更多详情here。 也可以参考my blog。

【讨论】:

几个月前,我在文档上有一个监听器,即使在调用 event.stopPropagation() 后仍会收到 mousedown 事件(我依赖于这种行为),但注意到在我更新后它不再被调用很多图书馆。我怀疑原因是 ReactJS 的更新,您的回答现在证实/解释了它。【参考方案7】:

我昨天遇到了这个问题,所以我创建了一个对 React 友好的解决方案。

查看react-native-listener。到目前为止,它运行良好。感谢您的反馈。

【讨论】:

react-native-listener 使用 findDomNode 将被弃用 github.com/yannickcr/eslint-plugin-react/issues/… 投反对票,因为答案不适合推向市场或提供不能补充完整答案的外部解决方案。【参考方案8】:

更新:你现在可以&lt;Elem onClick= proxy =&gt; proxy.stopPropagation() /&gt;

【讨论】:

见@RossAllen 的回答。这不会阻止传播到祖先的本机 DOM 侦听器(jQuery 使用)。 如果您使用的是 react 组件,那么这是正确的方法。劫持事件不是一个好主意。 如果其他事情失败了,那是因为你做错了其他事情 我同意,如果您的所有听众都通过 React 附加,这是正确的方法,但这不是这里的问题。 这只是我...我将始终鼓励正确使用正确的方法,而不是通过一种可能导致他们未来悲伤的工作而将某人误入歧途,因为他们有一个“解决方案”问题。【参考方案9】:

一种快速的解决方法是使用window.addEventListener 而不是document.addEventListener

【讨论】:

以上是关于ReactJS SyntheticEvent stopPropagation() 仅适用于 React 事件?的主要内容,如果未能解决你的问题,请参考以下文章

reactjs——解决setState异步问题

Reactjs API 请求问题

Reactjs 和 Mongodb 的连接

ReactJS:如何在 Jest 中通过动作触发减速器

如何通过构建现有状态来更新 reactjs 应用程序的状态?

如何在reactJs中的数组内部条件下禁用某些单选按钮