React 合成事件
如果DOM上绑定了过多的事件处理函数,整个页面响应以及内存占用可能都会受到影响。React为了避免这类DOM事件滥用,同时屏蔽底层不同浏览器之间的事件系统差异,实现了一个中间层——SyntheticEvent。
当用户在为onClick添加函数时,React并没有将Click事件绑定在对应的元素上面,而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装交给中间层SyntheticEvent(负责所有事件合成)
所有的react合成事件都存放在一个事件池里面,当调用的时候给合成事件的事件对象赋值,执行完之后把事件对象设置为null,放回事件池,而不用每次都创建一个新的合成事件
当事件触发的时候,对使用统一的分发函数ReactEventListener.dispatchEvent将指定函数执行。
合成事件和原生事件的执行顺序是什么?可以混用吗?
React的所有事件都通过document进行统一分发。当真实Dom触发事件后冒泡到document后才会对React事件进行处理。- 原生的事件会先执行,然后执行
React合成事件,最后执行真正在document上挂载的事件 React事件和原生事件最好不要混用。原生事件中如果执行了stopPropagation方法,则会导致其他React事件失效。因为所有元素的事件将无法冒泡到document上,导致所有的React事件都将无法被触发。 在React中event是一个SyntheticEvent,如果和它的交互被延迟了(例如:通过setTimeout),事件会被清除并且e.target引用不会再有效,事件对象上的所有属性都为null
import React from 'react'
class SyntheticEvent extends React.Component {
constructor(...props) {
super(...props)
}
clickHandler = (e) => {
setTimeout(() => {
console.log('React 事件触发了', e);
}, 2000)
}
render () {
return (
<>
<button onClick={this.clickHandler}>点击</button>
</>
)
}
}
export default SyntheticEvent

如果你需要在事件处理函数运行之后获取事件对象的属性,你需要调用 e.persist():
// ...
clickHandler = (e) => {
e.persist()
setTimeout(() => {
console.log('React 事件触发了', e);
}, 2000)
}

如果原生事件和React事件混用,则优先调用原生事件,再调用React事件,如果document上也绑定了原生事件,则最后执行;
import React from 'react'
class SyntheticEvent extends React.Component {
constructor(...props) {
super(...props)
this.btnRef = React.createRef()
}
clickHandler = (e) => {
console.log('React 事件触发了', e);
}
componentDidMount() {
this.btnRef.current.addEventListener('click', (e) => {
console.log('原生事件', e)
})
document.addEventListener('click', (e) => {
console.log('原生事件 document', e)
})
}
render () {
return (
<>
<button ref={this.btnRef} onClick={this.clickHandler}>点击</button>
</>
)
}
}
export default SyntheticEvent

如果原生事件中阻止冒泡了,则React事件不会触发
// ...
componentDidMount() {
this.btnRef.current.addEventListener('click', (e) => {
console.log('原生事件', e)
e.stopPropagation()
})
document.addEventListener('click', (e) => {
console.log('原生事件 document', e)
})
}

React v17 中,React 不会再将事件处理添加到 document 上,而是将事件处理添加到渲染 React 树的根 DOM 容器中:移除了 “event pooling(事件池)”
const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);
在 React 16 及之前版本中,React 会对大多数事件进行 document.addEventListener() 操作。React v17 开始会通过调用 rootNode.addEventListener() 来代替。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com