Reactのイベント処理を理解しよう

#目次

* [概要](#概要)
* [イベント処理とは](#イベント処理とは)
* [Reactのイベント処理](#reactのイベント処理)
* [イベントハンドラ一覧](#イベントハンドラ一覧)
* [まとめ](#まとめ)

#概要
この記事は、Reactにおけるイベント処理の仕組みなどについてまとめています。主にReact初心者向けの内容となっています。
また私自身の備忘録としてイベントハンドラについても一覧としてまとめていますので、参考にしてみてください。

#イベント処理とは
イベント処理とは、ユーザが「クリック」や「入力」などの操作が発生したときにプログラム側が行う処理のことを言います。

#Reactのイベント処理
Reactでのイベント処理はDOM要素のイベント処理と非常に似ていますが、いくつかの特徴があります。

* イベントはcamelCaseで名付ける（「onclick」ではなく「**onClick**」）
* イベントハンドラには文字列ではなく**関数を渡す**

簡単な例を見てみましょう。特に`<button>`タブの中身に注目して見てください。
**HTMLの場合**

```html
<html>
  <script>
    const buttonAlert = () => {
      alert('Clicked!');
    }
  </script>
  <body>
    <button onclick="buttonAlert()">Click me</button>
  </body>
</html>
```
**Reactの場合**

```react
cnost App = () => {
  const buttonAlert = () => {
    alert('Clicked!');
  }

  return(
    <button onClick={buttonAlert}>Click me</button>
  );
}
```
`<button>`タブの中身がHTMLの場合とReactの場合とではわずかに違いますよね。
ReactでHTMLの場合のように記述してしまうとエラーとなりますので気をつけましょう。

ReactはJavaScriptのライブラリなので、イベント処理では`addEventListener`を定義し、`onClick`などのイベント属性内に記述することは変なのではと疑問に感じる方もいるかもしれません。しかしReact(JSX)では、イベント処理を`onClick`等のイベント属性に記述します。なぜこのような記述方法が可能なのかですが、JSXがレンダリングされるとき実際には`addEventListener`に置換されるそうです。

上記の例のように状態などが無く、必ず同じ結果を返す場合は簡単に表現できます。
しかし状態によって処理に変更が生じる場合はどうなるでしょうか。

###メソッドのbind
はじめに例を見てみましょう。

```react:src/Toggle.js
import React, { Component } from 'react';

class Toggle extends Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // ここに注目
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

export default Toggle;
```
これはボタンが押されるたびにONとOFFを切り替える機能を実装したプログラムです。
このとき、`handleClick()`メソッド内では`this`を使って`state`を参照していますが、`this`は`constructor`内で使用されているので`handleClick()`メソッド内でも使用できるように関連付けてあげる必要があります。
このように`constructor`内で`handleClick()`メソッドを`bind`することで`this`を使用できるようになります。

ただメソッドが多くなればなるほど、`this.メソッド = this.メソッド.bind(this)`のような宣言も増えますよね。それを回避するため、つまり明示的に`bind`を記述しない記述方法があります。

方法その1：ES6のアロー関数をJSX内で記述する方法
方法その2：Class Fields利用する方法

それぞれ見てみましょう。
**方法その1**

```react:src/Toggle.js
import React, { Component } from 'react';

class Toggle extends Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
    // bindを書かなくて良い
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={() => this.handleClick()}>  {/* ここに注目 */}
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

export default Toggle;
```

**方法その2**

```react::src/Toggle.js
import React, { Component } from 'react';

class Toggle extends Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
    // bindを書かなくて良い
  }

  handleClick = () => {  // ここに注目
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

export default Toggle;
```
`bind`のある場合、方法その1、その2いずれの場合も同じ結果になります。`bind`のない場合の方がすっきり見えますし、記述量も少なくて済むので良いですね。

方法その1、その2のような記述ができる理由については、現時点で調べてもよくわからなかったので詳しい方がいればコメントなどで教えてもらえるとありがたいです。私のようによくわからないという人は、**そういう仕様**と捉えておけば大丈夫かとおもいます。

注意点として方法その2の手法はTC39というJavaScript関係の委員会が提案している手法なのですが、現在Stage3で実験的に行っているそうなので環境的にできない場合もあるかと思います。**Create React App**ではデフォルトで利用できます。

(補足)
方法その2のようにClass Fieldsを利用すると、クラス構文で`constructor`の宣言も省略することができます。

```react:src/Toggle.js
import React, { Component } from 'react';

class Toggle extends Component {
  // ここに注目(this.state → state)
  state = {isToggleOn: true};

  handleClick = () => {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

export default Toggle;
```
この場合propsはどう使うのか疑問に思うかもしれませんが、何も宣言せずに必要に応じて`this.props`も利用できます。


#イベントハンドラ一覧
Reactで用いることのできるイベントハンドラはJavaScriptのイベントハンドラに準拠しています。ではどのようなイベントハンドラがあるか見てみましょう。

| イベントハンドラ                      |      説明                       |    
|:--------------------------------:|:--------------------------------|
| onBlur / onFocus                 | フォーカスが移動したとき              |
| onChange                         | フォームの入力値 / 選択が変更されたとき |
| onSelect / onSelectStart         | テキストが選択されたとき              |
| onSubmit / onReset               | フォームの送信時 / リセット時          |
| onAbort / onError                | 画像読み込み時の中断 / エラー        |
| onLoad / onUnload                | ページの読み込み時 / 切り替え時       |
| onClick / onDblClick             | クリック / ダブルクリックされたとき       |
| onKeyPress / onKeyDown / onKeyUp | キーの状態に応じる                  |
| onMouseOver / onMouseOut         | マウスを乗せたとき / 離したとき        |
| onMouseDown / onMouseUp          | マウスでクリックしたとき               |
| onMousemove                      | マウスを動かしているとき              |

この辺りは覚えるというよりは、使いたいときに参照する程度にしておけばいいと思います。他にも色々あるので必要に応じて調べて使うようにしましょう。

#まとめ
今回はReactにおけるイベント処理とそのイベントハンドラについてまとめてみました。特に記述方法などの基礎的な部分に関してはしっかり抑えておきましょう。

* イベントはcamelCaseで名付ける（「onclick」ではなく「**onClick**」）
* イベントハンドラには文字列ではなく**関数を渡す**
* **bind**は記述方法によって**省略可能**


#参考資料

* [React公式サイト](https://ja.reactjs.org/docs/handling-events.html)
* [はじめてのReact #8「イベント処理を試してみよう」](https://blog.katsubemakito.net/react/react1st-8-event)
* [ReactをES6で開発時のbindの問題](https://qiita.com/konojunya/items/fc0cfa6a56821e709065)
* [Reactのコードを大幅に省略できるClassFieldsの挙動をチェック](https://qiita.com/tomoyamachi/items/358754ca36fd5fe6910c)
* [Class fields](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields)
* [JavaScriptのイベントハンドラ一覧](https://phpjavascriptroom.com/?t=js&p=event)