React 合成事件

  1. React 合成事件
  2. 合成事件和原生事件的执行顺序是什么?可以混用吗?

React 合成事件

如果DOM上绑定了过多的事件处理函数,整个页面响应以及内存占用可能都会受到影响。React为了避免这类DOM事件滥用,同时屏蔽底层不同浏览器之间的事件系统差异,实现了一个中间层——SyntheticEvent

当用户在为onClick添加函数时,React并没有将Click事件绑定在对应的元素上面,而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装交给中间层SyntheticEvent(负责所有事件合成)

所有的react合成事件都存放在一个事件池里面,当调用的时候给合成事件的事件对象赋值,执行完之后把事件对象设置为null,放回事件池,而不用每次都创建一个新的合成事件

当事件触发的时候,对使用统一的分发函数ReactEventListener.dispatchEvent将指定函数执行。

合成事件和原生事件的执行顺序是什么?可以混用吗?

  1. React的所有事件都通过 document进行统一分发。当真实 Dom触发事件后冒泡到 document后才会对 React事件进行处理。
  2. 原生的事件会先执行,然后执行 React合成事件,最后执行真正在 document上挂载的事件
  3. React事件和原生事件最好不要混用。原生事件中如果执行了 stopPropagation方法,则会导致其他 React事件失效。因为所有元素的事件将无法冒泡到 document上,导致所有的 React事件都将无法被触发。Reactevent 是一个 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