react有两种写法,一种是类组件,也叫有状态组件;一种是函数式组件,也叫无状态组件,而后引入了react hooks,函数式组件也能修改状态。在这两种组件中的事件绑定的原理一样,写法不一样。这篇文章主要就是记录这两种组件中的事件绑定写法。
重点是记录两种组件中事件绑定使用的区别,避免下次使用的时候还犯迷糊,在下面的写法中,可能出现同一类组件中多种写法,只要自己选择自己喜欢的那种写就可以了,没必要每种都要会,能看的懂就可以。想直接拿来用的,可以直接跳过原理,看使用案例,对原理感兴趣的可以回头来看看事件原理。在react中,事件的写法都是on+事件类型(第一个字母大写),如点击事件(onClick),鼠标事件(onMouseOver ,onMouseOut)
react中的事件都是合成事件,react并不是将click事件绑定到了div的真实DOM上,而是在document处监听了所有的事件,当事件发生并且冒泡到document处的时候,React将事件内容封装并交由真正的处理函数运行。这样的方式不仅仅减少了内存的消耗,还能在组件挂在销毁时统一订阅和移除事件。
另外冒泡到document上的事件也不是原生的浏览器事件,而是由react自己实现的合成事件(SyntheticEvent)。因此我们如果不想要是事件冒泡的话调用event.stopProppagation()方法是无效的。而应该调用event.preventDefault()。
组件更新或者装载时,在给dom增加合成事件时,需要将增加的target传入到document进行判断,给document注册原生事件回调为dispatchEvent(统一的事件分发机制)。
EventPluginHub负责管理React合成事件的callback,它将callback存储到listennerBank中,另外还存储了负责合成事件的Plugin,Event存储到listennerbank中,每一个元素在listennerBank中会有唯一的key。
点击时冒泡到docunment中,触发注册原生事件的回调dispatchEvent,获取到触发这个事件的最深层元素,事件执行利用react的批处理机制。
循环所有类型的eventPlugin,对应每个事件类型,生成不同的事件池,如果是空,则生成新的,有则用之前的,根据唯一key获取到指定的回调函数,再返回带有参数的回调函数。
-- > 组件装载/更新
-- > 新增/删除事件
-- > eventplugin添加到ListennerBank中监听事件
-- > 触发事件
-- > 生成合成事件
-- > 通过唯一key获取到指定函数
--> 执行指定回调函数
-- > 执行完毕后释放
import React, { Component } from 'react'
export default class App1 extends Component {
test2Fn() {
console.log('test2');
}
test3Fn = () => {
console.log('test3');
}
test4Fn = () => {
console.log('test4');
}
render() {
return (
<div>
{/* 直接写箭头函数,因为普通函数有this指向问题 */}
<button onClick={() => { console.log('test1'); }}>第一种</button>
{/* this.test2Fn()会被直接调用 和 this.test2Fn不一样*/}
<button onClick={this.test2Fn}>第二种</button>
<button onClick={this.test3Fn}>第三种</button>
<button onClick={() => {
this.test4Fn()
}}>第四种</button>
</div>
)
}
}
个人比较喜欢第三种写法
第一种写法有弊端,逻辑长的时候不方便观看,也不建写在dom里面
第二种有this指向问题,如定义一个a=100,在方法二中通过this.a获取会报错,这是因为this没有指向App1实例中
通过this.test2Fn.bind(this)可以解决,这样可以使两个this指向一致
import React, { Component } from 'react'
export default class App1 extends Component {
a=100 //添加一个全局变量
test2Fn() {
console.log('test2',this.a);
}
test3Fn = () => {
console.log('test3',this.a);
}
test4Fn = () => {
console.log('test4',this.a);
}
render() {
return (
<div>
{/* 直接写箭头函数,因为普通函数有this指向问题 */}
<button onClick={() => { console.log('test1',this.a); }}>第一种</button>
{/* this.test2Fn()会被直接调用 和 this.test2Fn不一样*/}
<button onClick={this.test2Fn.bind(this)}>第二种</button>
<button onClick={this.test3Fn}>第三种</button>
<button onClick={() => {
this.test4Fn()
}}>第四种</button>
</div>
)
}
}
call: 修改this指向,并使函数自动执行(不用函数()调用)
apply: 修改this指向,并使函数自动执行(不用函数()调用)
bind: 修改this指向,需要手动执行(用()调用)
function App2(){
const test=()=>{
console.log("add");
}
return(
<div >
<button onClick={test}>add</button>
</div>
)
}
export default App2