
2.5 事件处理
在BBS应用中的点赞功能已经涉及React中的事件处理,本节将详细介绍如何在React中处理事件。在React元素中绑定事件有两点需要注意:
(1)在React中,事件的命名采用驼峰命名方式,而不是DOM元素中的小写字母命名方式。例如,onclick要写成onClick,onchange要写成onChange等。
(2)处理事件的响应函数要以对象的形式赋值给事件属性,而不是DOM中的字符串形式。例如,在DOM中绑定一个点击事件这样写:

而在React元素中绑定一个点击事件变成这种形式:

React中的事件是合成事件,并不是原生的DOM事件。React根据W3C规范定义了一套兼容各个浏览器的事件对象。在DOM事件中,可以通过处理函数返回false来阻止事件的默认行为,但在React事件中,必须显式地调用事件对象的preventDefault方法来阻止事件的默认行为。除了这一点外,DOM事件和React事件在使用上并无差别。如果在某些场景下必须使用DOM提供的原生事件,可以通过React事件对象的nativeEvent属性获取。
其实,在React组件中处理事件最容易出错的地方是事件处理函数中this的指向问题,因为ES 6 class并不会为方法自动绑定this到当前对象。React事件处理函数的写法主要有三种方式,不同的写法解决this指向问题的方式也不同。
1.使用箭头函数
直接在React元素中采用箭头函数定义事件的处理函数,例如:

因为箭头函数中的this指向的是函数定义时的对象,所以可以保证this总是指向当前组件的实例对象。当事件处理逻辑比较复杂时,如果把所有的逻辑直接写在onClick的大括号内,就会导致render函数变得臃肿,不容易直观地看出组件的UI结构,代码可读性也不好。这时,可以把逻辑封装成组件的一个方法,然后在箭头函数中调用这个方法。代码如下:

直接在render方法中为元素事件定义事件处理函数,最大的问题是,每次render调用时,都会重新创建一个新的事件处理函数,带来额外的性能开销,组件所处层级越低,这种开销就越大,因为任何一个上层组件的变化都可能会触发这个组件的render方法。当然,在大多数情况下,这点性能损失是可以不必在意的。
2.使用组件方法
直接将组件的方法赋值给元素的事件属性,同时在类的构造函数中,将这个方法的this绑定到当前对象。例如:

这种方式的好处是每次render不会重新创建一个回调函数,没有额外的性能损失。但在构造函数中,为事件处理函数绑定this,尤其是存在多个事件处理函数需要绑定时,这种模板式的代码还是会显得烦琐。
有些开发者还习惯在为元素的事件属性赋值时,同时为事件处理函数绑定this,例如:

使用bind会创建一个新的函数,因此这种写法依然存在每次render都会创建一个新函数的问题。但在需要为处理函数传递额外参数时,这种写法就有了用武之地。例如,下面的例子需要为handleClick传入参数item:

3.属性初始化语法(property initializer syntax)
使用ES 7的property initializers会自动为class中定义的方法绑定this。例如:

这种方式既不需要在构造函数中手动绑定this,也不需要担心组件重复渲染导致的函数重复创建问题。但是,property initializers这个特性还处于试验阶段,默认是不支持的。不过,使用官方脚手架Create React App创建的项目默认是支持这个特性的。你也可以自行在项目中引入babel的transform-class-properties插件获取这个特性支持。