React 事件系统


用户界面除了展示页面信息外,还要响应用户事件。React会将事件处理程序绑定到组件上来处理事件。事件被触发的同时,更新组件的内部状态。组件内部状态的更新,会触发组件的重绘。借助这些机制,我们能够轻松的响应用户输入,并根据用户输入内容完成用户界面的更新。


  1. 事件处理器的绑定
  2. 事件处理与参数传递
  3. 合成事件与原生事件
  4. 组件的DOM事件临听


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中的大部分事件都不需要额外处理就能工作,但是触控事件(onTouchCancelonTouchEndonTouchMove 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的过程,将事件处理器自动绑定到组件实例上。