ライフサイクルメソッドがわかればReactの50%は理解したと言えなくもない

## ライフサイクルの話をする前に・・・

まず前提として下記のことが言えると思う。

Reactとはそもそも、状態の管理、更新を適切に描画に反映するためのライブラリである。さらに、Componentの最も大きな役割は描画（render）をすることである。

効率良く描画の表示、更新をするためにはComponentのライフサイクルを理解することが必須であると言える。

## ライフサイクルとは

**クラスコンポーネント**を扱う際にライフサイクルは大きく分けて3つの期間がある。順番にMounting(マウント時)、Updating(更新時)、Unmounting(マウント解除時)。

公式でも下記のような図が紹介されているのでこちらを元に説明をする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47844/b018cf5f-0d60-bded-cf2f-0f9c2a649d45.png)

[React lifecycle methods diagram](http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/)



### Mounting(マウント時)

Componentが表示されるまでの期間のこと。

1. constructor()
2. getDerivedStateFromProps() // 一般的ではないライフサイクル
3. render() // ここで描画される
4. componentDidMount()

componentDidMountで描画された後に何かしらの変更を加えたい、描画されてから出ないとできないことをここで行うことになる。ちなみに、最初にrenderされるときだけこの期間に入る。

例えばユーザー一覧を表示させるUIだとしたら、基本的なTableの見出しとユーザーの取得が完了するまでの間に表示させる `Loading...` などがMounting時のrenderに相当する。そこまでの描画が完了したら `componentDidMount` でユーザー一覧のAPIを叩く。


### Updating(更新時)

Componentが表示され、Stateの更新を行う期間のこと。

1. getDerivedStateFromProps() // 一般的ではないライフサイクル
2. shouldComponentUpdate() // 一般的ではないライフサイクル
3. render()
4. getSnapshotBeforeUpdate() // 一般的ではないライフサイクル
5. componentDidUpdate()

Stateの更新がされたら、1 ~ 5の順番でメソッドが呼ばれ再描画される。

先ほどの例でいけば、`componentDidMount` でユーザー一覧のAPIを叩いた後に、取得が完了すればStateのusersにデータが入ることになる。

``` javascript
state = {
  users: [
    // ここの部分に入る
  ]
}
```

このusersが `[]` から `[{id: 1, name: 'hogehoge'}]` と更新されたことを検知して再描画 > TableにUserを表示させることができる。

では、`componentDidUpdate()` はどのタイミングで使うべきなのか例を挙げてみる。

ユーザーを新たに追加したいとしよう。UIでは一覧上で `新規登録` ボタンを押すとモーダルが出てきて ユーザー名を入力 > 登録とすることで追加することができるとする。

その場合、登録を押すことで APIを叩いてpostしているわけだが、送り終わったことがわかるStateの変更を検知して、ユーザー一覧の再取得をすることができる。

``` javascript
componentDidUpdate(prevProps) {
  if(!prevProps.succeeded && this.props.succeeded) {
    // ユーザー一覧を再取得
  }
}
```

このときわかりにくいのが、prevPropsである。読んで字のごとく以前のPropsである。では、どういうことかというと `componentDidUpdate` では更新される以前のPropsまたはStateと更新後のPropsまたはStateを比較することができる。それにより、特定のState（Props）が更新されたときにだけ何かしらの処理をすることができる。


### Unmounting(マウント解除時)

他のComponentに移るときにだけ呼ばれる期間。

1. componentWillUnmount()

現在のコンポーネントを破棄する直前に呼ばれるメソッド。

よく見落とされるのがアニメーションやタイマーを設定を `ComponentDidMount` でしていた場合は `componentWillUnmount` で破棄すること。そうしないと新しいコンポーネントのサイクルが始まった後も、その分のメモリが開放されないままになってしまう。



## unsafeメソッドについて

実は以前は、他にもライフサイクルメソッドが存在していた。（今もしているけど非推奨）例えば、

- componentWillMount()
- componentWillUpdate()
- componentWillReceiveProps()

である。これらを自身のプロジェクトで見つけたらこっそり書き換えてあげると良い。

ちなみに、完全に個人的な見解だけど、結局ライフサイクルメソッドがたくさんあると、どこで何が変更されたなど、タイミングが難しくなってしまうことが多々ある。人に優しくないコードが簡単にできてしまうのではないかなと。

なので、基本的には `componentDidMount`, `componentDidUpdate` でまかなえるようにComponentの設計をすべきであると思う。

その流れが hooksのuseEffectに引き継がれているのかなと。


## Functional Componentの場合のライフサイクル

以前までは クラスコンポーネントでないとライフサイクルを使うことができなかった。が、hooksの登場により Functional Component でもライフサイクルを使うことができるようになった。

`useEffect`を使うことで簡単に 特定Stateの更新を検知することができる。

``` javacript
React.useEffect(() => {
  // なんかやる    
}, [// 検知したいState ])

```

例えば、エラーコードが検知したら何かやりたい場合は以下のような感じになる。

``` javascript
const [code, setErrCode] = React.useState<string>('')
  React.useEffect(() => {
    if (code) {
      // codeが変更されたら何かやる
    }
  }, [code])
```

## ライフサイクルメソッドの問題点？

状態の管理、更新を適切に描画をするのには必須と言えるライフサイクルメソッドだが、問題が発生するケースもある。

よくあるのが、componentDidUpdate内でifが乱立していて更新の検知が適切に行えているのかわかりにくいことがあげられる。

この問題の根本はライフサイクルメソッドが悪いのではなくComponentの設計に問題があることがほとんど。

Componentがやたらと大きくなりすぎていて多機能すぎていると言える。

次回はComponentの設計あたりを記事にする予定・・・