Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
111
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

@koba04

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

この通り、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の扱いについて書きたいと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
111
Help us understand the problem. What are the problem?