106
104

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

一人React.jsAdvent Calendar 2014

Day 9

React.jsのEventについて

Posted at

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

↑のページにあるイベントは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);

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

touch event

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

React.initializeTouchEvents(true)

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


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

106
104
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
106
104

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?