处理事件
通过 React 元素处理事件跟在 DOM 元素上处理事件非常相似。但是有一些语法上的区别:
- React 事件使用驼峰命名,而不是全部小写。
- 通过 JSX , 你传递一个函数作为事件处理程序,而不是一个字符串。
例如,HTML:
<button onclick="activateLasers()">
Activate Lasers
</button>
在 React 中略有不同:
<button onClick={activateLasers}>
Activate Lasers
</button>
另一个区别是,在 React 中你不能通过返回 false
(愚人码头注:即 return 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 规范 定义了这个合成事件,所以你不需要担心跨浏览器的兼容性问题。查看 SyntheticEvent
参考指南了解更多。
当使用 React 时,你一般不需要调用 addEventListener
在 DOM 元素被创建后添加事件监听器。相反,只要当元素被初始渲染的时候提供一个监听器就可以了。
当使用一个 ES6 类 定义一个组件时,通常的一个事件处理程序是类上的一个方法。例如, 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(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
在JSX回调中你必须注意 this
的指向。 在 JavaScript 中,类方法默认没有 绑定 的。如果你忘记绑定 this.handleClick
并将其传递给onClick
,那么在直接调用该函数时,this
会是 undefined
。
这不是 React 特有的行为;这是 JavaScript 中的函数如何工作的一部分。 一般情况下,如果你引用一个后面没跟 ()
的方法,例如 onClick={this.handleClick}
,那你就应该 绑定(bind) 该方法。
如果调用 bind
令你烦恼,有两种方法可以解决这个问题。 如果您使用实验性的 属性初始化语法 ,那么你可以使用属性初始值设置来正确地 绑定(bind) 回调:
class LoggingButton extends React.Component {
// 这个语法确保 `this` 绑定在 handleClick 中。
// 警告:这是 *实验性的* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
这个语法在 创建 React App 中是默认开启的。
如果你没有使用属性初始化语法,可以在回调中使用一个 箭头函数:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
这个语法的问题是,每次 LoggingButton
渲染时都创建一个不同的回调。在多数情况下,没什么问题。然而,如果这个回调被作为 prop(属性) 传递给下级组件,这些组件可能需要额外的重复渲染。我们通常建议在构造函数中进行绑定,以避免这类性能问题。
将参数传递给事件处理程序
在循环内部,通常需要将一个额外的参数传递给事件处理程序。 例如,如果 id
是一个内联 ID,则以下任一方式都可以正常工作:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上述两行代码是等价的,分别使用 arrow functions 和 Function.prototype.bind
。
上面两个例子中,参数 e
作为 React 事件对象将会被作为第二个参数进行传递。通过箭头函数的方式,事件对象必须显式的进行传递,但是通过 bind
的方式,事件对象以及更多的参数将会被隐式的进行传递。