React核心概念:事件处理
事件处理函数
处理React元素的事件与处理DOM元素上的事件十分相似,它们仅有一些语法上的区别。
- React事件名采用小驼峰命名规则而不是纯小写
- 使用JSX需要传入函数作为事件处理函数而不是传入字符串
例如,在HTML上
<button onclick="activateLasers()">
Activate Lasers
</button>
在React中则略微不同
<button onClick={activateLasers}>
Activate Lasers
</button>
React上的另一个不同之处在于你不能返回false
去阻止默认行为,必须显示地调用preventDefault
才能阻止默认行为。
在HTML中,为了阻止锚点链接默认打开一个新页面可以这样写:
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
但是在React中,需要这样写才能阻止打开新页面:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
在这里,e
是一个合成事件。React根据W3C规范
来定义这些合成事件,所以无需担心浏览器的兼容问题。
使用React时,通常不需要调用addEventListener
来为已创建的DOM元素添加监听器。取而代之的是在元素被渲染之初提供一个监听器。
当你定义了一个ES6 class组件时,一个常用的方法是将事件处理器声明为class的一个方法。下面的代码声明了一个Toggle
组件,让用户能在ON和OFF两种状态之间切换。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 这段代码让this在回调函数中能够有效
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
必须谨慎对待JSX回调函数中的this
,在Javascript中,class方法是不会默认绑定this
的。如果你忘记绑定this.handleClick
并将它传递给onClick
,那么当这个方法被调用时浏览器将无法识别this
,因为此时this
是undefined
。
这不是React特有的特性,这与JavaScript的工作原理相关。通常来说,如果不在方法后面加上()
,比如onClick={this.handleClick}
,我们就应该绑定它。
如果觉得绑定方法很麻烦,这里有两种选择可以避免绑定。如果你正在使用实验性的public class fields语法
,你可以使用class fields
正确绑定回调。
class LoggingButton extends React.Component {
// 这个语法可以确保回调函数已经绑定了this
// 但请注意这是实验性的语法
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
这种语法在使用Create React App创建的项目中是可以使用的。
如果没有使用class fields语法
,那么你可以在回调函数中使用箭头函数。
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 这段代码确保了handleClick绑定了this
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
但是这个语法有一个问题,每当LoggingButton被渲染时都会创建一个不同回调函数。在大多数情况下这是没有问题的,但是如果这个回调函数作为props传递给子组件时,这些组件可能会做一个额外的重新渲染。所以我们通常推荐在构造函数中绑定事件或者使用class fields语法来绑定以避免此类的性能问题。
向事件处理函数传递参数
在循环中我们通常需要传递一个额外的参数给事件处理函数。比如如果id时想要删除的那一行的id,以下两种方法都可以将id传递给事件处理函数。
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上面两行代码是等价的,只不过分别使用了箭头函数
和Function.prototype.bind
。
在上面的两个例子中,合成事件e代表了React事件,都作为第二个参数传递给事件处理函数。在箭头函数中我们需要显示地传递e,但是使用bind方法,事件对象以及更多的参数都被隐式地传递。