Reactのメインコンセプト要点まとめ(2)

# この記事の位置づけ
React公式ドキュメントの「メインコンセプト」について、実際にReactの開発を行う中で特に参考になった章を要点の解説付きで纏めたものである。実際にReact公式ドキュメントを読み解いてもらうのが一番だが、時間がない人に要点だけでも読んでもらえるようにしたいと思い、纏めている。

公式のメインコンセプトでは、以下の章立てで説明をしている。
1. Hello World
2. JSXの導入
3. 要素のレンダー
4. コンポーネントとprops
5. stateとライフサイクル
6. イベント処理
7. 条件付きレンダー
8. リストとkey
9. フォーム
10. stateのリフトアップ
11. コンポジションvs継承
12. Reactの流儀

当記事では、以下の3章分について要点まとめをしている。
4. コンポーネントとprops
5. stateとライフサイクル
6. イベント処理

# 出典元資料
React公式ドキュメントの[メインコンセプト](https://ja.reactjs.org/docs/hello-world.html)

# メインコンセプト
## 4. コンポーネントとprops
### 知っておくべきポイント
#### コンポーネントの種類
Reactのコンポーネントには「関数コンポーネント」と「クラスコンポーネント」の二種類がある。

##### 関数コンポーネント
関数コンポーネントには以下の特徴がある。

* JavaScriptの関数として定義する
* propsを引数として受け取ることができる
* stateは持たない
* React要素を返す

```jsx:function-comp.js
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
```

##### クラスコンポーネント
クラスコンポーネントには以下の特徴がある。

* React.Componentクラスを継承したクラスとして定義する
* propsを引数として受け取ることができる
* stateを持つ
* render()関数でReact要素の[イミュータブルな置き換え](https://qiita.com/s_harada/items/8283f4a2844a79e26c49#%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%95%E3%82%8C%E3%81%9Freact%E8%A6%81%E7%B4%A0%E3%81%AE%E6%9B%B4%E6%96%B0)を定義する

##### 関数コンポーネントとクラスコンポーネントの使い分け
以下の基準で関数コンポーネントとクラスコンポーネントを使い分けるのがよいと考えている。

* コンポーネントに状態を持たせる必要があるか？
  * **状態を持たせる必要がない**なら、関数コンポーネントを使用する
  * **状態持たせる必要がある**なら、クラスコンポーネントを使用する

* リファクタリングで[stateのリフトアップ](https://qiita.com/s_harada/items/a89cd4238d9435b47a59)を実施した場合
  * 子のコンポーネントは**stateを持たなくなる**ので、関数コンポーネントに変更する
  * 親のコンポーネントは**stateを持つ必要がある**ので、クラスコンポーネントを使用する

関数コンポーネントは状態を持たないので**ステートレスコンポーネント**、クラスコンポーネントは状態を持つので**ステートフルコンポーネント**と一般的に呼ばれる。

[stateのリフトアップ](https://qiita.com/s_harada/items/a89cd4238d9435b47a59)でstateはできるだけ親に集約することを推奨している通り、ステートフルコンポーネントはできるだけ少なくし、基本的にステートレスコンポーネントでUIを構成することが望ましい。

#### ユーザ定義のコンポーネントを使用する
以下の例のように、`Welcome`というコンポーネントを定義してレンダリングすることが可能。
ユーザ定義のコンポーネントを定義することで、コンポーネント単位のUI分割や再利用が可能となる。
※以下の例でもWelcomeコンポーネントを3回再利用している

```jsx:welcom.js
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
```

React公式は、できるだけUI部品を小さなコンポーネントに分割することを推奨している。
その理由としては以下の通り。

* コンポーネントが大きくなると、React要素内のネストが深くなるため変更が難しくなる
* 個々のUI部品を再利用するのが困難になる

React公式は、経験則から以下のケースでコンポーネントの分割と再利用を検討すべきとしている。

* 複数回使われるUI（`Button`、`Panel`、`Avatar`など）
* UI自体が複雑である場合（`App`、`Comment`など）

#### コンポーネントのpropsは変更してはいけない
Reactのルールとして、**すべてのReactコンポーネントは自身のpropsを変更してはいけない**という取り決めがある。これは、Reactのコンポーネントは**純粋関数**でなければいけないというルールがあるためである。

##### 純粋関数とは？
渡された値を変更せず、必ず同じ結果を返す関数を**純粋関数**と呼ぶ。

以下のような関数は、必ずa+bの結果を返すので純粋関数と呼べる。

```sum.js
function sum(a, b) {
  return a + b;
}
```

対して、以下のような関数は渡された値を変更しているため、純粋関数とは呼べない。

```withdraw.js
function withdraw(account, amount) {
  account.total -= amount;
}
```

### 一言コメント
React公式チュートリアルをやっただけでは、以下の2点は曖昧なままなのでやはりメインコンセプトは読んでおく価値があると思う。

* Reactコンポーネントには関数コンポーネントとクラスコンポーネントが存在する
* Reactコンポーネントは純粋関数でなければいけない

## 5. stateとライフサイクル
### 知っておくべきポイント
#### クラスコンポーネントの作成
Reactコンポーネントに状態を持たせる場合はクラスコンポーネントを使うというのは、「4. コンポーネントとprops」の章で紹介したとおり。
クラスコンポーネントは以下のような構成となっている。

```jsx:clock.js
class Clock extends React.Component {  //ポイント1
  render() {  //ポイント2
    return (  //ポイント3
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock date={new Date()} />,
  document.getElementById('root')
);
```

ポイントは以下の3点。
1. React.Componentを継承するクラスを作成する
2. render()メソッドを定義し、returnでReact要素を返す
3. コンポーネントのpropsにはthis.propsでアクセスする

#### クラスコンポーネントにローカルのstateを追加する
上記のクラスコンポーネントには状態をまだ持たせていない。
コンポーネントの状態（ローカルのstate）を持たせると、以下のような構成となる。

```jsx:clock.js
class Clock extends React.Component {
  constructor(props) {
    super(props);  //ポイント1
    this.state = {date: new Date()};  //ポイント2
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>  //ポイント3
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);
```

ポイントは以下の3点。
1. コンストラクタで親クラスにpropsを渡す
2. コンポーネントのローカルstate（`this.state`）に値を格納する
3. ローカルstateには`this.state`でアクセスする

#### クラスコンポーネントのライフサイクルメソッド
多数のコンポーネントを有するアプリケーションの場合、コンポーネントが不要となったときにコンポーネントが保持しているリソースを開放することが重要。
それを実現するために、Reactのクラスコンポーネントは**ライフサイクルメソッド**という仕組みを持っている。

```jsx:clock.js
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};  //ポイント１
  }

  componentDidMount() {  //ポイント２
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {  //ポイント３
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);
```

ポイントは以下の２点。

* Clockコンポーネントのローカルstateに現在日付をセットしている
* ClockコンポーネントがDOMとして描画された後に`componentDidMount()`メソッドが実行される
  * このことを、Reactではマウント(mounting)と呼ぶ
  * この例では、`this.timerID`に１秒のタイマーをセットしている
  * タイマーが１秒過ぎると`tick()`が実行され、ローカルstateの日時が更新される
     * これによって、ClockコンポーネントのDOMが描画されてから１秒後に日時が更新されることになる
* Clockコンポーネントが生成したDOMが削除されるときに`componentWillUnmount()`メソッドが実行される
  * このことを、Reactではアンマウント(unmounting)と呼ぶ
  * この例では、`this.timerID`の１秒タイマーをリセットしている
     * これによって、ClockコンポーネントのDOMが描画されるとタイマーがリセットされることになる

#### stateを正しく使用する
Reactコンポーネントのstateを扱うにあたって、いくつか知っておくべきことがある。

##### stateは直接変更しない
`this.state`の値を直接変更してはいけない。直接変更すると、Reactは値の変更を検知できないためDOMが再レンダーされない。
`this.state`の値を直接変更してよいのは、Reactコンポーネントのコンストラクタ内だけである。
（コンストラクタで`this.state`に初期値を設定する処理は再レンダーの必要がないため）

`this.state`の値を変更したい場合、`this.setState()`を使用する。`this.setState()`による値の変更はReactが検知できるため、値の変更によってDOMの再レンダーが行われる。

```ng.js
// this.stateを直接変更するのはコンストラクタ内以外ではNG
this.state.comment = 'Hello';
```

```ok.js
// this.stateの値変更はthis.setState（）で行う
this.setState({comment: 'Hello'});
```

##### stateの更新は非同期に行われることがある
Reactは`this.props`と`this.state`を非同期で更新するため、stateの値変更が`this.props`と`this.state`の値に依存していると意図しない動作になる恐れがある。

例えば、以下のケースでは`this.state.counter`の値変更が`this.state.counter`の値と`this.props.increment`の値に依存している。
`this.props`と`this.state`は非同期で更新されるので、`this.state.counter`の加算が非同期で実行される前に、次の加算処理が行われる可能性がある。そうすると、処理結果が意図しないものになる恐れがある。

```ng.js
this.setState({
  counter: this.state.counter + this.props.increment,
});
```

この問題を回避するために、`this.setState()`には`this.state`や`this.props`の値を使用しての値変更を行う際に同期的に値変更を行う方法が用意されている。
以下のように、`this.setState`に関数（第1引数にstate、第2引数にprops）を渡すことで、stateの値変更は値が確定している状態のstateとpropsが使われる。

```ok.js
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));
```

##### 単方向データフロー
いかなるstateも必ず特定のコンポーネントが所有しており、stateから生ずるすべてのデータやUIは、コンポーネントの親子関係における子のコンポーネント側にのみ影響するデータフローとなっている。
このようなデータフローは一般的には**単方向データフロー**と呼ばれている。

コンポーネントのツリー構造において、React公式はデータフローのイメージを「propsをデータが流れ落ちる滝であるとすれば、各コンポーネントのstateは任意の場所から合流してくる追加の水源であり、それもまた下に流れ落ちていくもの」と表現している。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329353/1ad34a4a-4714-db43-237c-b4b697fc7fab.png)

### 一言コメント
React公式チュートリアルをやっていれば、stateを直接変更してはならないことは知っているはず。
ただ、ライフサイクルメソッドという機能についてはチュートリアルだけでは知らないままなので、この機能は是非抑えておきたい。

## 6. イベント処理
### 知っておくべきポイント
#### Reactにおけるイベント処理
onclickなどのDOM要素のイベントと同様の動作を、Reactのイベント処理として設定することが可能。

##### DOM要素のonclickイベントでactivateLasers関数を実行する例

```click.js
<button onclick="activateLasers()">
  Activate Lasers
</button>
```

##### ReactのonClickイベント処理でactivateLasers関数を実行する例

```jsx:click.js
<button onClick={activateLasers}>
  Activate Lasers
</button>
```

#### JSXのコールバックにおける`this`の扱い
Reactのイベント処理でクラスメソッドを呼び出す場合、以下の3つの方法がある。

##### 方法1：クラスコンポーネントのコンストラクタでthisを束縛しておく
コンストラクター内で`this.handleClick`で使用するthisを束縛しておく。
ただ、この方法だとすべてのクラスメソッドについてthisを束縛しておく必要があるので、クラスメソッドの数が多い場合はあまり現実的ではない。

```jsx:toggle.js
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    this.handleClick = this.handleClick.bind(this);  //コンストラクタで各クラスメソッドにthisをbindする
  }

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

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

##### 方法2：パブリッククラスフィールド構文を使用する（Create React Appの場合はデフォルトでこの書き方が可能）
クラスメソッドをアロー演算子の関数として定義しておくことで、thisを予め束縛しておくことが可能。
Create React Appでアプリを初期生成した場合、この構文はBabelで予め有効になっている。

```jsx:logginbutton.js
class LoggingButton extends React.Component {
  handleClick = () => {  //handleClickメソッドをアロー演算子の関数として定義することでthisを束縛する
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}
```

##### 方法3:Reactのイベント処理でアロー関数を使用する（パブリッククラスフィールド構文を使用していない場合はこの手法を取る）
JSX内のReactイベント処理をアロー関数で記述することで、thisを束縛した状態でクラスメソッドを呼び出すことができる。

```jsx:loggingbutton.js
class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={() => this.handleClick()}>  //Reactイベント呼び出し時にアロー演算子を使用することでthisを束縛する
        Click me
      </button>
    );
  }
}
```

#### Reactのイベントハンドラに引数を渡す
Reactのイベント処理に対して、引数を渡すことができる。
第1引数は任意の値、第2引数はReactイベントを渡す。

```jsx:click.js
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
```

### 一言コメント
Reactによるイベント処理は、クリックしたときやフォーカスが外れたときの動作を定義したいときによく使う。thisの束縛については、Create React Appを使用しているのなら「パブリッククラスフィールド構文」を使用しておけば間違いないと思うが、thisはJSの理解を深めるために避けて通れないので、他の書き方についても理解しておいたほうがよいと思う。
また、Reactのイベントハンドラに引数を渡すのはよくあるので、これも書き方を覚えておいたほうがよい。