React中的事件捕获和冒泡
假设你有以下代码:
const [counter, setCounter] = useState(0);
return (
<div onClick={() => setCounter((prevCounter) => prevCounter + 1)}>
<button onClick={() => setCounter((prevCounter) => prevCounter + 1)}>
Counter value is: {counter}
</button>
</div>
);
这样就渲染出了这个按钮:
点击该按钮后会显示什么?
如果你猜“计数器值为:1”,那就错了!
我们得到的结果是这样的:
但为什么?
理解事件传播
在我们的示例中,虽然我们点击了 `<div>` 元素button,但它的父元素的事件处理程序div也被触发了。这是因为事件不仅仅影响触发事件的目标元素——它们会在 DOM 树中上下传递,最终到达目标元素。
这被称为事件传播:一种定义事件如何传播或在 DOM 树中传递以到达目标元素,以及之后会发生什么情况的机制。
事件传播的概念是为了处理DOM层级结构中多个具有父子关系的元素对同一事件(例如鼠标点击)都有事件处理程序的情况而引入的。现在的问题是,当用户点击内部元素时,哪个元素的点击事件会先被处理:是外部元素的点击事件,还是内部元素的点击事件?
事件传播分为三个阶段:
- 捕获阶段 - 事件从
window底部开始,直到到达顶部event.target。 - 目标阶段 - 事件已到达目标阶段
event.target。引发事件的最深层嵌套元素称为目标元素,可通过以下方式访问event.target: - 冒泡阶段——事件从
event.target元素向上冒泡,直到到达父元素window,这意味着:当一个元素上发生事件时,它首先会处理该元素的事件,然后处理其父元素的事件,再向上层处理其父元素的事件。这与捕获阶段的过程正好相反。
React中的事件冒泡和捕获
React 对冒泡和捕获的支持方式与 DOM 规范中的描述相同,只是附加处理程序的方式有所不同。
冒泡机制与普通的 DOM API 一样简单;只需将处理程序附加到元素的最终父元素,该元素上触发的任何事件都会冒泡到父元素,就像我们开头示例中那样。
捕获操作同样简单直接,但onClick需要将属性应用onClickCapture到元素上,而不是应用到 prop 上。
如何阻止事件冒泡/捕获?
回到我们最初的例子,我们如何确保点击按钮时计数器只加 1 呢?
答案是使用stopPropagation()
此接口方法Event可以阻止当前事件在捕获和冒泡阶段的进一步传播。
但是,它不会阻止任何默认行为的发生。(如果您想阻止这些行为,则需要使用相应的preventDefault()方法。)
如果我们把代码改成:
const [counter, setCounter] = useState(0);
return (
<div onClick={() => setCounter((prevCounter) => prevCounter + 1)}>
<button
onClick={(event) => {
event.stopPropagation();
setCounter((prevCounter) => {
return prevCounter + 1;
});
}}
>
Counter value is: {counter}
</button>
</div>
每次点击按钮,计数器都会加 1,这样event.stopPropagation()可以防止事件冒泡到button父级并触发父级onClick。
但是,在阻止事件传播时要小心,因为有时你无法确定你不会在某个元素的父元素中需要上述事件,而这可能用于完全不同的目的。
如果情况属实,阻止传播的一种替代方法是在一个处理程序中将数据写入事件对象,并在另一个处理程序中读取它,这样就可以将有关下面处理的信息传递给父处理程序。
祝你编程愉快!🚀
文章来源:https://dev.to/eladtzemach/event-capturing-and-bubbling-in-react-2ffg


