React.jsのEventについて

今回はDOMのイベントの扱いについて書きたいと思います。

## SyntheticEvent

React.jsではDOMをVirtualDOMとしてwrapしているようにDOMのイベントについてもSyntheticEventとしてwrapしていて、クロスブラウザ対応されています。

I/Fはこんな感じです

>>
boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
Number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
void stopPropagation()
DOMEventTarget target
Date timeStamp
String type



この通り、`preventDefault`や`stopPropagation`、`target`などもいつものように使うことが出来ます。

ちなみにイベントの伝播を止める時にfalseを返したりすることもあるかもしれないですが、この挙動はわかりにくいということでv0.12からReact.jsでは使えなくなっているので注意が必要です。

## イベントハンドラ

基本的なイベントはサポートされていて、例えばClickイベントを処理したい場合はこんな感じです。

```js
var Counter = React.createClass({
  getInitialState() {
    return {
      count: 0
    };
  },
  onClick(e) {
    // e is SyntheticEvent
    this.setState({ count: this.state.count + 1 });
  },
  render() {
    return (
      <div>
        <span>click count is {this.state.count}</span>
        <button onClick={this.onClick}>click!</button>
      </div>
    );

  }
});
```

`onClick={this.onClick}`とすることでクリックイベントを受け取っているのですが、この時`onClick`の中で`this.setState`している通り、`this`はReact.jsがComponentのインスタンスにbindしてくれます。
なので`onClick(e) {...}.bind(this)`などとする必要はありません。

* ちなみにこの`this`を自動的にbindする挙動は将来的にES6のArrowFunction使うようになってなくなるかもしれません。


## Event delegation

Event delegation自体はjQueryなどで使っていた人も多いのではないでしょうか。

React.jsではrootの要素にだけイベントリスナを登録してそこで全部受けて、内部的に持っているマッピング情報をもとに対象のComponentでイベントを発行しています。

その時イベントはキャプチャリング〜バブリングしていくわけですが、各リスナ毎にSyntheticEventのオブジェクトが作られるため、メモリのアロケートを何度も行う必要があります。
その対策として、React.jsでは開始時にオブジェクトをpoolしてそれを使いまわす実装になっていて、ガベージコレクションの回数を減らす工夫がされています。

ちなみにマッピングにはおそらくDOMに設定されているdata-reactidが使われていて、IDで親子関係がわかるようなものになっています。

```html
<ul class="nav nav-pills nav-justified" data-reactid=".1px6jd5i1a8.1.0.0.0.1.0">
  <li class="" data-reactid=".1px6jd5i1a8.1.0.0.0.1.0.0">
    <a href="/artist" data-reactid=".1px6jd5i1a8.1.0.0.0.1.0.0.0">Artist</a>
  </li>
  <li class="" data-reactid=".1px6jd5i1a8.1.0.0.0.1.0.1">
    <a href="/country" data-reactid=".1px6jd5i1a8.1.0.0.0.1.0.1.0">Country</a>
  </li>
</ul>
```
http://react-serverside-rendering.herokuapp.com/ より


## Not provided event

http://facebook.github.io/react/docs/events.html

↑のページにあるイベントはReact.jsでサポートされているので普通に使えるのですが、windowのresizeイベントやjQuery Plugin独自のイベント等を使いたい場合は、`componentDidMount`で`addEventListener`などを使ってイベントを登録して`componentWillUnmount`で`removeEventListener`するなどしてイベントを解除する必要があります。

ちなみにこのときも`this`は自動的にbindしてくれています。

```js
var Box = React.createClass({
  getInitialState() {
    return {
      windowWidth: window.innerWidth
    };
  },
  handleResize(e) {
    this.setState({windowWidth: window.innerWidth});
  },
  componentDidMount() {
    window.addEventListener('resize', this.handleResize);
  },
  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize);
  },
  render() {
    return <div>Current window width: {this.state.windowWidth}</div>;
  }
});
React.render(<Box />, mountNode);
```
http://facebook.github.io/react/tips/dom-event-listeners.html

ただ、この辺りについてはissueもあったりするのでサポートされることもあるかもしれません。

https://github.com/facebook/react/issues/285

## touch event

touch系のイベントはデフォルトでは対象になっていないので対象にしたい場合は、

```js
React.initializeTouchEvents(true)
```

を呼んでおく必要があります。


-----

今回はEventについて紹介しました。
明日はFormの扱いについて書きたいと思います。