Skip to content
kaba

React合成事件

React1 min read

React的事件系统采用了事件委托的思想,除了媒体类的事件无法被事务系统处理外,大部分事件都不会被绑定在具体元素上,而是统一绑定在document上。事件在dom节点上触发,冒泡到document上,document上的统一事件处理程序会将事件分发到具体的组件实例。分发前,react会对事件进行包装。

触发顺序

原生 -> 合成 -> document

绑定事件

  1. diff时,通过registrationNameModule判断props是否有事件类型
1props: {
2 className: "btn-primary",
3 onClick: function ..., // 事件类型
4 onBlur: function ... //事件类型
5}
6// registrationNameModule
7{
8 onBlur: SimpleEventPlugin,
9 onClick: SimpleEventPlugin,
10 onClickCapture: SimpleEventPlugin,
11 onChange: ChangeEventPlugin,
12 onChangeCapture: ChangeEventPlugin,
13 onMouseEnter: EnterLeaveEventPlugin,
14 onMouseLeave: EnterLeaveEventPlugin,
15 ...
16}
  1. 通过registrationNameDependencies检查事件依赖了哪些原生事件
1// registrationNameDependencies
2{
3 onBlur: ['blur'],
4 onClick: ['click'],
5 onClickCapture: ['click'],
6 onChange: ['blur', 'change', 'click', 'focus', 'input', 'keydown', 'keyup', 'selectionchange'],
7 onMouseEnter: ['mouseout', 'mouseover'],
8 onMouseLeave: ['mouseout', 'mouseover'],
9 ...
10}
  1. 检查这个事件是否注册过,如果没有,就挂在document上,将React提供的dispatchEvent回调

触发事件

  1. 任意一个事件触发,执行 dispatchEvent 函数。
  2. dispatchEvent 执行 batchedEventUpdates(handleTopLevel)batchedEventUpdates 会打开批量渲染开关并调用 handleTopLevel
  3. handleTopLevel 会依次执行 plugins 里所有的事件插件。
  4. 如果一个插件检测到自己需要处理的事件类型时,则处理该事件。

  1. React 的合成事件只能在事件周期内使用,因为这个对象很可能被其他阶段复用, 如果想持久化需要手动调用event.persist() 告诉 React 这个对象需要持久化。( React17 中被废弃)
  2. React 的冒泡和捕获并不是真正 DOM 级别的冒泡和捕获
  3. React 会在一个原生事件里触发所有相关节点的 onClick 事件, 在执行这些onClick之前 React 会打开批量渲染开关,这个开关会将所有的setState变成异步函数。
  • 事件只针对原生组件生效,自定义组件不会触发 onClick
1const handleClick = e => {
2 setTimeout(()=>{
3 console.log(e.target.value)
4 },100)
5} //错误,因为事件处理完后已经销毁
6
7// React在派发事件时会批量更新,所有的setState都会变成异步的
8// 1,2 异步,render1次 3,4同步,render2次
9const handleClick = () => {
10 setState({num: 1});
11 setState({num: 2});
12 setTimeout(()=>{
13 setState({num: 3});
14 setState({num: 4});
15 },100)
16}

React17

调整将顶层事件绑定到container上,而不是document上