今回は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イベントを処理したい場合はこんな感じです。
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独自のイベント等を使いたい場合は、componentDidMount
でaddEventListener
などを使ってイベントを登録してcomponentWillUnmount
でremoveEventListener
するなどしてイベントを解除する必要があります。
ちなみにこのときも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の扱いについて書きたいと思います。