ReactのSyntheticEventとは何なのか
Reactでイベントを作るとき、引数にe
を渡す例がよくある。
例えば公式ドキュメントにもこんなコードが載ってる。
function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); }
このe
はSyntheticEvent
を表すものらしいが、どんなものなのかいまいちつかめないのでいろいろ調べてみた。
特にこのページがわかりやすく、役に立った。
通常のイベントとの違い
Reactを使わず、通常のJSでイベントを実装するときは、イベントの種類によって様々なインターフェイスがある。
例えばクリックイベントを作るならMouseEvent
を使い、キーボード入力をイベントリスナーとするならKeyBoardEvent
を使う。
イベントインターフェイスの種類によって、独自のプロパティが実装されており、ブラウザごとの対応状況も異なる。そのため、ネイティブのJSでイベントをクロスブラウザ対応させるのはなかなか大変らしい。
ReactのSynthetic Event
一方Reactの場合、全てのイベントをSyntheticEvent
として扱う。
Synthetic Event
を使えば、各ブラウザのネイティブイベントを全てラップしており、何も意識しなくてもクロスブラウザ対応ができる。
そして冒頭に出てきたe
は、イベントが発生した際にそれをSyntheticEvent
としてイベントハンドラに渡す役割がある。
単なる引数にすぎないので名前は何でもいい。event
にしている人もいた。
SyntheticEventのプロパティ
SyntheticEvent
に用意されているプロパティは以下のような感じ。
boolean bubbles boolean cancelable DOMEventTarget currentTarget boolean defaultPrevented number eventPhase boolean isTrusted DOMEvent nativeEvent void preventDefault() boolean isDefaultPrevented() void stopPropagation() boolean isPropagationStopped() DOMEventTarget target number timeStamp string type
どのネイティブイベントにもあてはまりそうな、一般性の高いものが選ばれている。
ネイティブイベント特有のプロパティを使うには
個々のネイティブイベント特有のプロパティを使いたい場合も問題ない。
SyntheticEvent
が、イベントの名前によって何のイベントをラップしているか識別し、それに合わせてプロパティを使えるようになる。
例えば、公式が作ったコードを少し修正して、以下のようなコードを書いてみた。
もともとのコードは、ボタンをクリックするとオン・オフが切り替わるというだけのものだったが、
今回の修正により、shiftキーを押しながらクリックしないと切り替わらないようにしてみた。
See the Pen Toggle - ShiftKey ver. by Udomomo (@Udomomo) on CodePen.
修正したのは以下の部分。
handleClick
関数に引数e
を渡し、e.shiftKey
で条件分岐させてみた。
handleClick(e) { // eを追加 if (e.shiftKey) { // e.shiftKeyによる条件分岐を追加 this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); } }
なぜこれが動くかというと、render
関数の中でonClick
という名前でイベントを定義しているため。
これにより、SyntheticEvent
が「自分がラップしているネイティブイベントはonMouseEvent
だ」と認識し、ネイティブJSのMouseEvent
オブジェクトのプロパティであるShiftKey
を使えるようになったのだ。
イベントの名前とプロパティの対応関係は、公式ドキュメントに載っている。
SyntheticEventのPoolingについて
公式ドキュメントには、「The SyntheticEvent is pooled」とある。
これは、SyntheticEventオブジェクト自体が再利用されるということ。
SyntheticEvent
のコールバック関数の実行が終わると、プロパティの値が全てnull
にリセットされる。
すなわち、あるSyntheticEvent
の中でプロパティに値を設定した後、それを非同期で呼び出す別の処理で使ったり、値を取っておいて後で別の関数の中で使おうとしても、null
しか返ってこない。
event.persist()
を使えば、非同期処理でもプロパティの値を参照できるようになるらしいが、Reactのパフォーマンスが落ちてしまうようだ。