![React进阶之路](https://wfqqreader-1252317822.image.myqcloud.com/cover/752/26793752/b_26793752.jpg)
2.5 事件处理
在BBS应用中的点赞功能已经涉及React中的事件处理,本节将详细介绍如何在React中处理事件。在React元素中绑定事件有两点需要注意:
(1)在React中,事件的命名采用驼峰命名方式,而不是DOM元素中的小写字母命名方式。例如,onclick要写成onClick,onchange要写成onChange等。
(2)处理事件的响应函数要以对象的形式赋值给事件属性,而不是DOM中的字符串形式。例如,在DOM中绑定一个点击事件这样写:
![](https://epubservercos.yuewen.com/700037/15253388305240606/epubprivate/OEBPS/Images/Figure-T52_3344.jpg?sign=1739164195-6nj8qpQNpADsr5SkPgL3hwmxutsw8gpt-0-0405a9e99f40395d5ecdb09852c8ff70)
而在React元素中绑定一个点击事件变成这种形式:
![](https://epubservercos.yuewen.com/700037/15253388305240606/epubprivate/OEBPS/Images/Figure-T52_796.jpg?sign=1739164195-1ivHuMCZpAc0mcrG56IEA7NT9z6MoWbT-0-1cd98f23c891a76444792e0eee877497)
React中的事件是合成事件,并不是原生的DOM事件。React根据W3C规范定义了一套兼容各个浏览器的事件对象。在DOM事件中,可以通过处理函数返回false来阻止事件的默认行为,但在React事件中,必须显式地调用事件对象的preventDefault方法来阻止事件的默认行为。除了这一点外,DOM事件和React事件在使用上并无差别。如果在某些场景下必须使用DOM提供的原生事件,可以通过React事件对象的nativeEvent属性获取。
其实,在React组件中处理事件最容易出错的地方是事件处理函数中this的指向问题,因为ES 6 class并不会为方法自动绑定this到当前对象。React事件处理函数的写法主要有三种方式,不同的写法解决this指向问题的方式也不同。
1.使用箭头函数
直接在React元素中采用箭头函数定义事件的处理函数,例如:
![](https://epubservercos.yuewen.com/700037/15253388305240606/epubprivate/OEBPS/Images/Figure-T52_3346.jpg?sign=1739164195-kmVhxzOHj6m5UCpnTf2dSjBQxY7zuYFg-0-4416bc83b9b9fe95d6e1db4b7ceaf48b)
因为箭头函数中的this指向的是函数定义时的对象,所以可以保证this总是指向当前组件的实例对象。当事件处理逻辑比较复杂时,如果把所有的逻辑直接写在onClick的大括号内,就会导致render函数变得臃肿,不容易直观地看出组件的UI结构,代码可读性也不好。这时,可以把逻辑封装成组件的一个方法,然后在箭头函数中调用这个方法。代码如下:
![](https://epubservercos.yuewen.com/700037/15253388305240606/epubprivate/OEBPS/Images/Figure-T53_3349.jpg?sign=1739164195-AzFIEuSkNHeLHjhrdBfUZ6qRKYtK586u-0-cf9c9afaac4d8e065ee893f7193ea3bb)
直接在render方法中为元素事件定义事件处理函数,最大的问题是,每次render调用时,都会重新创建一个新的事件处理函数,带来额外的性能开销,组件所处层级越低,这种开销就越大,因为任何一个上层组件的变化都可能会触发这个组件的render方法。当然,在大多数情况下,这点性能损失是可以不必在意的。
2.使用组件方法
直接将组件的方法赋值给元素的事件属性,同时在类的构造函数中,将这个方法的this绑定到当前对象。例如:
![](https://epubservercos.yuewen.com/700037/15253388305240606/epubprivate/OEBPS/Images/Figure-T54_3352.jpg?sign=1739164195-4ZupZKLnthdOOawR4nVNQDxD2RrSlGEz-0-311af760c31a1b3ace59493b287af98a)
这种方式的好处是每次render不会重新创建一个回调函数,没有额外的性能损失。但在构造函数中,为事件处理函数绑定this,尤其是存在多个事件处理函数需要绑定时,这种模板式的代码还是会显得烦琐。
有些开发者还习惯在为元素的事件属性赋值时,同时为事件处理函数绑定this,例如:
![](https://epubservercos.yuewen.com/700037/15253388305240606/epubprivate/OEBPS/Images/Figure-T54_3355.jpg?sign=1739164195-TYoqHdc8Rplc8nQFkN0epcmFaGZRT7rI-0-4b1fbe14d854a8c9dc9e424980d0ba96)
使用bind会创建一个新的函数,因此这种写法依然存在每次render都会创建一个新函数的问题。但在需要为处理函数传递额外参数时,这种写法就有了用武之地。例如,下面的例子需要为handleClick传入参数item:
![](https://epubservercos.yuewen.com/700037/15253388305240606/epubprivate/OEBPS/Images/Figure-T55_3357.jpg?sign=1739164195-gHZ0ZOsp6aQqYs0uT9o3LrFvR2MWIk5d-0-e11b2894e9f3cf8b6fe2bb8b732514cb)
3.属性初始化语法(property initializer syntax)
使用ES 7的property initializers会自动为class中定义的方法绑定this。例如:
![](https://epubservercos.yuewen.com/700037/15253388305240606/epubprivate/OEBPS/Images/Figure-T56_3360.jpg?sign=1739164195-KTj0lxp1t3VmhBZw9HiHIQMATFO0xYhH-0-4d7f38d69e16fd4afb82c27c6b582ee2)
这种方式既不需要在构造函数中手动绑定this,也不需要担心组件重复渲染导致的函数重复创建问题。但是,property initializers这个特性还处于试验阶段,默认是不支持的。不过,使用官方脚手架Create React App创建的项目默认是支持这个特性的。你也可以自行在项目中引入babel的transform-class-properties插件获取这个特性支持。