Reactのライフサイクルを実際に書いて確認してみた

ReactのライフサイクルをChromeのConsoleで確認してみました。
#確認方法
- create-react-appで確認用アプリの雛形を作る
- アプリ内にライフサイクルメソッドを配置し、呼ばれる度に```console.log('【メソッド名】 now!')```を出力する

#扱うメソッド
- constructor()
- render()
- componentDidMount()
- componentDidUpdate()
- shouldComponentUpdate()
- componentWillUnmount()

※「componentWillMount()」などの非推奨とされるメソッドは扱いません

#アプリの作成&起動
```
> create-react-app LifecycleTest
> cd LifecycleTest
> yarn start
```
#src/Test.jsを作成
src/Test.jsの中身

```javascript

import React, { Component } from 'react';

class Test extends Component {
    constructor(props) {
        super(props);
        this.state = {
            clicked: false,
        }
        this.handleClick = this.handleClick.bind(this);
        console.log('constructor now!');
    }

    componentDidMount() {
        console.log('componentDidMount now!');
    }

    handleClick() {
        this.setState({
            clicked: true,
        })
    }

    componentDidUpdate() {
        console.log('componentDidUpdate now!')
    }

    shouldComponentUpdate() {
        console.log('shouldComponentUpdate now!')
        return true;
    }

    render() {
        console.log('render now!');
        return (
            <button onClick={this.handleClick}>Click</button>
        );
    }
}

export default Test;
```
#src/App.jsの編集
- 同一階層から、Testコンポーネントをインポート
- ```<header></header>```の真下に```<Test/>```を配置

```javascript

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Test from './Test'

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
        <Test/>
      </div>
    );
  }
}

export default App;
```

#Chromeブラウザで確認
ブラウザで起動（同時にマウント）し、Consoleをみると「constructor」→「render」→「componentDidMount」の順でメソッドが呼ばれていることがわかる。
<img width="554" alt="スクリーンショット 2018-11-28 19.19.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/246362/6bcda27d-3c33-d44e-7641-cd2a0d1496c0.png">

デフォルトのヘッダーの下にあるボタンを押下すると、あらかじめ設定しておいた```this.state.clicked: false```が、```this.state.clicked: true```にアップデートされる。
<img width="407" alt="スクリーンショット 2018-11-28 19.18.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/246362/6ac5bd76-df61-8e30-b09b-b47477510c49.png">

クリックによってアップデートされると、先ほどのマウント時に引き続き「shouldComponentUpdate」→「render」→「componentDidUpdate」の順でメソッドが呼び出されている。
<img width="553" alt="スクリーンショット 2018-11-28 19.30.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/246362/1bb3b755-bf25-b8a6-6eb6-ec8e0c477c35.png">
#componentWillUnmountの確認

これまでで
- constructor()
- render()
- componentDidMount()
- componentDidUpdate()
- shouldComponentUpdate()
のライフサイクルが確認できたので、最後に「componentWillUnmount()」を確認します。

#src/App.jsを編集
- ``constructor()``を追加
- ``handleUnmount()``を追加
- ``<button></button>``を配置
- ``<Test/>``コンポーネント表示に条件を付加

```javascript

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Test from './Test'

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      renderChild: true,
    }
    this.handleUnmount = this.handleUnmount.bind(this);
  }

  handleUnmount() {
    this.setState({
      renderChild: false,
    });
  }

  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
        <button onClick={this.handleUnmount}>Unmount Child</button>
        {this.state.renderChild ? <Test number={this.state.number}/> : null}
      </div>
    );
  }
}

export default App;
```
#src/Test.jsを編集
- src/Test.jsに``componentWillUnmount()``の追加

```javascript
componentWillUnmount() {
  console.log('componentWillUnmount now!')
}
```

#もう一度ブラウザで確認

再度Chrome上でマウントし、「Click」ボタン→「Unmount Child」ボタンの順に押下することで、このような順序でログが表示される。
<img width="552" alt="スクリーンショット 2018-11-29 13.49.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/246362/be2e6967-a045-e46b-1775-3b51c529a4bb.png">

#まとめ
- 個人的によく使う「constructor」、「render」、「componentDidMount」あたりの呼び出されるタイミングがミニマムに確認できた
- 「componentWillUnmount」と「shouldComponentUpdate」は初めて使ってみたので、イベントに対する挙動が確認できた