React教程 - 6. 事件处理

本文译自React官方文档
全文翻译及相关代码,请参看我的Github

React元素的事件处理与DOM元素很类似,但有一些语法差异:

  • React事件采用驼峰命名
  • 使用JSX,我们可以直接传递函数而非字符串作为事件处理程序

例如,HTML:

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

这在React中稍有不同:

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

另一个不同之处在于,React中我们无法通过返回false阻止默认行为。
我们必须显式的调用preventDefault。例如,在纯HTML中,为防止链接打开新页面的默认行为,我们可以这么写:

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

在React中的写法:

1
2
3
4
5
6
7
8
9
10
11
12
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}

return (
<a href="#" onClick={handleClick}>
Click me
</a>
);

}

这里的e是一个事件元素。React根据W3规范定义这一事件,因此我们无需担心浏览器的兼容问题。
在使用React时,在一个DOM创建后,需要为其增加监听事件时,我们通常不采用addEventListener,而是在元素最初被渲染时提供一个监听器。
当我们采用一个ES6 class定义一个组件时,通常的做法是把事件处理函数作为该类的一个方法。
例如,下面这个Toggle组件渲染了一个点击切换开关状态的按钮:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};

// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}

render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);

}
}

ReactDOM.render(
<Toggle />,
document.getElementById('root')
);

在CodePen中尝试
注意JSX回调中的this
在JavaScript中,类方法不会默认绑定this。
如果忘记绑定this.handleClick而将其直接传递给onClick,当函数被调用时,this的值是undefined的。
这并不是React的特有行为,具体可参看这篇文章.
通常来说,如果我们引用一个没有()的方法,如onClick = {this.handleClick},我们应该绑定该方法。
如果不想采用bind的方式,这里有另外两种选择。
如果你在使用实验性初始化语法,则可以使用属性初始化器完成正确的回调绑定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class LoggingButton extends React.Component {
// 这个表达式确保this绑在handleClick中
// 注意!!! 这个是实验性语法
handleClick = () => {
console.log('this is:', this);
}

render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);

}
}

另一种方法是在回调中使用箭头函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}

render() {
// 这个表达式确保this绑在handleClick中
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);

}
}

使用箭头函数的问题在于这种方式会导致每次渲染LogginButton时都会创建一个不同的回调函数。
在大多情况下,这么做没什么问题。但是,如果这个回调函数是作为属性被传递给更底层的组件时,该组件可能会被影响,造成一次额外的重新渲染。
为避免这种问题的发生,我们推荐采用在构造函数中手动绑定或使用属性初始化语法。

本文首发于http://www.miaoyunze.com/,转载请注明出处