用户界面除了展示页面信息外,还要响应用户事件。React会将事件处理程序绑定到组件上来处理事件。事件被触发的同时,更新组件的内部状态。组件内部状态的更新,会触发组件的重绘。借助这些机制,我们能够轻松的响应用户输入,并根据用户输入内容完成用户界面的更新。
1. 事件处理器的绑定
React 事件处理本质上还是JavaScript的事件。
React 对事件进行了统一化,使事件在不同浏览器上有一致的属性。给组件添加一个事件处理器,就像添加一个普通属性一样。事件命名与原生DOM事件名一致且会在相同情景下被触发,并使用驼峰形式表示,如:'onChange'、'onClick'等。
1.1 JSX中的事件绑定
JSX语法中绑定事件处理器的语法与HTML语法非常类似。如,为按钮添加一个点击事件:
<button class="btn btn-save" onClick={this.handleSaveClicked}>保存</button>
在上面示例中,当用户点击按钮时,组件的handleSaveClicked方法会被调用。
注意:JSX语法绑定事件处理器时,在写法上类似于DOM0级的事件绑定。在DOM编程中并不推荐这种写法,但React并没有使用HTML的'onClick'等属性,React只是使用了这种绑定方式,而其内部则按照需要高效的维护着事件处理器。
1.2 非JSX中的事件绑定
如果不使用JSX语法,那么可以在组件元素的参数对象上指定事件处理器。如:
React.DOM.button({className:'btn btn-save', onClick:this.handleSaveClicked}, '保存');
1.3 React支持的事件及事件初始化
React对各种事件类型都提供了友好的支持,具体支持的事件类型请参考官方文档:事件系统。
React中的大部分事件都不需要额外处理就能工作,但是触控事件(onTouchCancel、onTouchEnd、onTouchMove、 onTouchStart)需要调用以下代码进行初始化:
React.initializeTouchEvents(true);
2. 事件处理与参数传递
2.1 事件处理的使用
接下来,我们定义一个LikeButton组件,并为其'onClick'事件添加处理程序handleClick。在这个处理函数中,改变组件的liked状态:
class LikeButton extends React.Component {
constructor(props) {
super(props);
this.state = {liked: false};
}
handleClick(e) {
this.setState({ liked: !this.state.liked });
}
render() {
const text = this.state.liked ? '喜欢' : '不喜欢';
return (
<p onClick={this.handleClick.bind(this)}>
你<strong>{text}</strong>这个,点击更改。
</p>
);
}
}
ReactDOM.render(
<LikeButton />,
document.getElementById('example')
);
在上例中,我们在使用ES6 Class语法定义了LikeButton组件。在组件的handleClick处理函数中修改了组件this.state.liked状态。组件的state改变后,会自动触发组件的render()方法重新渲染组件。
注意,在上例中我们使用bind(this)显式的绑定了事件处理函数的运行上下文,当使用ES6 Class语法定义组件时,这是必须的操作。
2.2 参数传递
bind()方法可以在绑定this时传递额外的参数:
render: function() {
return <p onClick={this.handleClick.bind(this, 'extra param')}>;
},
handleClick: function(param, event) {
// handle click
}
3. 合成事件与原生事件
3.1 原生事件
原生事件,即:浏览器原生DOM事件。原生事件在组件的componentDidMount方法中通过addEventListener,并在componentWillUnmount方法中通过removeEventListener移除。
3.2 合成事件
React 实现了一个合成事件层(SyntheticEvent),SyntheticEvent是对浏览器原生事件跨浏览器的封装。React 事件处理程序通过SyntheticEvent实例进行传递,通过这个事件模型保证了和 W3C 标准保持一致,消除了不同浏览器之间的兼容问题。所以不用担心有什么诡异的用法,并且这个事件层消除了 IE 与 W3C 标准实现之间的。
合成事件会以事件委托(event delegation)的方式绑定到组件最上层,并且在组件卸载(unmount)的时候自动销毁绑定的事件。
所有通过JSX方式绑定的事件都是绑定到合成事件,除非有特别的需要,否则建议总是用 React 合成事件的方式处理事件。
4. 组件的DOM事件临听
React 支持添加原生DOM事件,原生事件在componentDidMount方法中通过addEventListener绑定,并应该在componentWillUnmount方法中通过removeEventListener移除。
以下是一个自动获取浏览器窗口宽度的示例:
var Box = React.createClass({
getInitialState: function() {
return {windowWidth: window.innerWidth};
},
handleResize: function(e) {
this.setState({windowWidth: window.innerWidth});
},
componentDidMount: function() {
window.addEventListener('resize', this.handleResize);
},
componentWillUnmount: function() {
window.removeEventListener('resize', this.handleResize);
},
render: function() {
return <div>当前窗口宽度: {this.state.windowWidth}</div>;
}
});
ReactDOM.render(
<Box />,
document.getElementById('example')
);
componentDidMount会在组件渲染完成且已经有了 DOM 结构的时候被调用。通常情况下,我们可以在这时绑定普通的 DOM 事件。这种绑定方式事件是被绑定在React组件,而不是原始原素上。React 通过一个autobinding的过程,将事件处理器自动绑定到组件实例上。
