React.jsのEventについて

  • 93
    Like
  • 0
    Comment
More than 1 year has passed since last update.

今回は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

この通り、preventDefaultstopPropagationtargetなどもいつものように使うことが出来ます。

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

イベントハンドラ

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

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で親子関係がわかるようなものになっています。

<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独自のイベント等を使いたい場合は、componentDidMountaddEventListenerなどを使ってイベントを登録してcomponentWillUnmountremoveEventListenerするなどしてイベントを解除する必要があります。

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

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系のイベントはデフォルトでは対象になっていないので対象にしたい場合は、

React.initializeTouchEvents(true)

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


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