此文章是翻译handling-events这篇React(版本v16.2.0)官方文档。

Handling Event

React 元素处理事件和DOM 元素事件处理非常类似。只有几个语法不同:

  • React 事件以camelCase 方式命名,而不是lowercase。
  • 在JSX 中以函数作为事件句柄(event handler),而不是字符串

例子,这HTML:

  1. <button onclick="activateLasers()">
  2. Activate Lasers
  3. </button>

同React 有轻微不同:

  1. <button onClick={activateLasers}>
  2. Activate Lasers
  3. </button>

另一个不同就是在React 中你不能通过返回false 来阻止默认事件发生。你必须明确的调用preventDefault。例如,在纯HTML 中,阻止一个链接跳转打开一个新页面,你可以这样写:

  1. <a href="#" onclick="console.log('The link is clicked.'); return false">Click me</a>

在React 中,可以通过以下方式来代替:

  1. function ActionLink(){
  2. function handleClick(e){
  3. e.preventDefault();
  4. console.log('The link was clicked.');
  5. }
  6. return (
  7. <a href="#" onClick={handleClick}>
  8. Click me
  9. </a>
  10. );
  11. }

这里e 是一个合成事件(synthetic event)。React 根据W3C spce来定义合成事件(synthetic event),所以你不必担心跨浏览器适配(cross-browser compatibility)问题。参考SyntheticEvent 了解更多。

在使用React 时通常你不需要在一个DOM 元素创建之后,调用addEventListener去添加一个侦听器。相反,只需要在元被初次渲染时添加一个侦听器就可以了。

当你使用ES6 class 定义一个组件时,通常的做法是在类中添加一个方法作为事件句柄。例如,这个Toggle 组件就是渲染一个按钮(button)让用户在“ON”和“OFF”状态之间切换:

  1. class Toggle extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {isToggleOn: true};
  5. // This binding is necessary to make `this` work in the callback
  6. this.handleClick = this.handleClick.bind(this)
  7. }
  8. handleClick() {
  9. this.setState(prevState => ({
  10. isToggleOn: !prevState.isToggleOn
  11. }));
  12. }
  13. render() {
  14. return (
  15. <button onClick={this.handleClick}>
  16. {this.state.isToggleOn ? 'ON' : 'OFF'}
  17. </button>
  18. );
  19. }
  20. }
  21. ReactDOM.render(
  22. <Toggle />,
  23. document.getElementById('root')
  24. );

在CodePen 上尝试

你必须注意在JSX 回调函数中this 的含义。在JavaScript 中,类方法并不是默认绑定的(bound)。如果你忘记绑定this.handleClick 并把它传递给onClick,在实际被调用时,this 的值将会是undefined

这并不是React-specific(特性)行为;它只是how functions work in JavaScript。通常如你引用一个函数并没有带()在其函数后面,例如onClick={this.handleClick} ,你需要绑定这方法。

如果调用bind 令你烦恼,这里有两种方法让你绕开它。如果你正在使用试验性的公共类字段语法,你可以使用类字段去正确的绑定回调函数:

  1. class LoggingButton extends React.Component {
  2. // This syntax ensures `this` is bound within handleClick
  3. // Waring: this is *experimental* syntax.
  4. handleClick = () => {
  5. console.log('This is:', this);
  6. }
  7. render() {
  8. return (
  9. <button onClick={this.handleClick}>
  10. Click me
  11. </button>
  12. );
  13. }
  14. }

这个语法在Create React App 是默认可用的。

如果你不使用类字段语法,你可以在回调函数中使用箭头函数(arrow function):

  1. class LoggingButton extends React.Component {
  2. handleClick() {
  3. console.log('This is:', this);
  4. }
  5. render() {
  6. // This syntax ensures 'this' is bound within handleClick
  7. return(
  8. <button onClick={(e)=>this.handleClick(e)}>
  9. Click me
  10. </button>
  11. );
  12. }
  13. }

这个语法的问问题是当LoggingButton 每一次被渲染时,都会创建一个不同的回调函数。在大多数情况下,这是可以承受的。然而,如果这个函数被作为prop 传递给子组件(lower components),这些组件可能还会做一次额外的渲染(an extra re-rendering)。我们通常建议在构造函数中绑定或使用类字段语法,来避免这类性能问题。

Passing Arguments to Event Handlers

在一个循环中,通常想要传递额外的参数到事件处理器中。例如,如果id 是行ID,下面两种方法都可以工作:

  1. <button onClick={(e) => this.deleleRow(id, e)}>Delete Row</button>
  2. <button onClick={this.deleleRow.bind(this, id)}>Delete Row</button>

尚明两行是相等的,分别使用arrow functionsFunction.prototype.bind

在这两个例子中,e 参数作为React 事件将会作为ID 之后的第二个参数被传入。在箭头函数中,我们必须显示地传入,但是在bind 中,任何更多的参数被自动地提前。