参考:
从 React 绑定 this,看 JS 语言发展和框架设计
React 事件处理
事件包含了许多细节,在实际开发中容易忽略,造成不是我们期望的结果,本文包含了这部分的细节,解决开发中遇到的 React
事件的问题。
对比传统 HTML
React
元素的事件处理和 DOM
元素的很相似。但是有一点语法上的不同:
React
事件绑定属性的命名采用驼峰式写法,而不是小写。- 如果采用
JSX
的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM
元素的写法)
传统的 HTML
:
1 | <button onclick="activateLasers()">Activate Lasers</button> |
React
中稍稍有点不同:
1 | <button onClick={activateLasers}>Activate Lasers</button> |
阻止事件默认行为
不能使用返回 false
的方式阻止默认行为。你必须明确的使用 preventDefault
传统 HTML
中可以这样写
1 | <a href="#" onclick="console.log('The link was clicked.'); return false"> |
在 React
中应该这样写
1 | function ActionLink() { |
这里的参数 e
是合成事件,React
根据 W3C spec
来定义这些合成事件,所以你不需要担心跨浏览器的兼容性问题。
在 ES6 中的事件处理
1 | class Toggle extends React.Component { |
你必须谨慎对待 JSX 回调函数中的 this,类的方法默认是不会绑定 this 的。如果你忘记绑定 this.handleClick 并把它传入 onClick, 当你调用这个函数的时候 this 的值会是 undefined。
这并不是 React 的特殊行为;它是函数如何在 JavaScript 中运行的一部分。通常情况下,如果你没有在方法后面添加 () ,例如 onClick={this.handleClick},你应该为这个方法绑定 this。
绑定 this
React.createClass 自动绑定
React.createClass
创建的组件,可以自动绑定 this
。也就是说,this
这个关键字会自动绑定在组件实例上面。
1 | // This magically works with React.createClass |
当然很遗憾,对于组件的创建,官方已经推荐使用 class
声明组件或使用 functional
无状态组件
渲染时绑定
1 | onChange = {this.handleChange.bind(this)} |
有一个潜在的性能问题: 当组件每次重新渲染时,都会有一个新的函数被创建。但在正常开发场景中,可以忽略。
箭头函数绑定
1 | onChange = {e => this.handleChange(e)} |
同样存在潜在的性能问题
Constructor 内绑定
1 | constructor(props) { |
重复而累赘
Class 属性中使用 = 和箭头函数
1 | handleChange = () => { |
这种方式的优点:
- 使用箭头函数,有效绑定了
this
; - 没有第二种方法和第三种方法的潜在性能问题;
- 避免了方法四的组件实例重复问题;
- 我们可以直接从
ES5 createClass
重构得来。
这个方法依赖于 ES next 的新特性, 请参考:tc39.github.io > http://babeljs.io/blog/2015/06/07/react-on-es6-plus/ > http://babeljs.io/blog/2015/10/31/setting-up-babel-6/
好消息是在 create-react-app 等工具中已经默认开启。
向事件处理程序传递参数
1 | <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> |
上述两种方式是等价的,分别通过 arrow functions
和 Function.prototype.bind
来为特定事件类型添加事件处理程序。
上面两个例子中,参数 e
作为 React
事件对象将会被作为第二个参数进行传递。通过箭头函数的方式,事件对象必须显式的进行传递,但是通过 bind
的方式,事件对象以及更多的参数将会被隐式的进行传递。
值得注意的是,通过 bind
方式向监听函数传参,在类组件中定义的监听函数,事件对象 e
要排在所传递参数的后面,例如:
1 | class Popper extends React.Component { |
其他问题
事件自动执行了?
例如:
1 | <a href="#" onClick={this.onLoadQR(text)}> |
这种写法是直接执行了函数,所以每一次渲染都会执行
而正确的行为是,我们应该在这里进行事件绑定,因为 React
不会进行自动绑定,具体方法看前文 “绑定 this
”
不写 this
为什么找不到方法?
例如:
1 | <Switch defaultChecked onChange={onChangeSwitch(text)} /> |
省略了 this
, 此时方法虽然写在了 class
内,但是缺无法获取到,此时如果将方法写到 class
外,作为一个全局变量,但这个方法内部的 this
指向缺为 undefined
。