React HooksのuseEffectで関数コンポーネントにライフサイクルを持たせる

#目次

- [はじめに](#はじめに)
- [useEffectとは](#useeffectとは)
- [useEffectが呼び出されるタイミング](#useeffectが呼び出されるタイミング)
- [useEffectの使い方](#useeffectの使い方)
- [useEffectで非同期処理をする](#useeffectで非同期処理をする)
- [まとめ](#まとめ)

#はじめに
この記事では、React 16.8で導入された**Hooks**の機能の一つで、関数コンポーネントにライフサイクルメソッドを記述することのできる**`useEffect`**についてまとめています。現在ReactやHooksについて学習中の方の一助となれば幸いです。

###前提条件

- Reactについて基本的な部分を理解している
 - Reactの基本については[こちら](https://qiita.com/jima-r20/items/48220ec1980535280a66)
 - 「Hooksとは」については[こちら](https://qiita.com/jima-r20/items/663cfab5e8db53294843)

###環境
| 導入    | version  |
|:------:|:--------:|
| react  | 16.13.1  |

#useEffectとは
**`useEffect`**は簡単にいうと、**関数コンポーネントにライフサイクルメソッドを持たせるための機能**です。
Hooksが導入される以前のReactでは、コンポーネントにライフサイクルメソッドを持たせるためにはクラスコンポーネントで記述する必要がありました。そのためはじめは関数コンポーネントで記述していたコンポーネントにライフサイクルイベントを追加する必要が出てきたときにわざわざクラスコンポーネントに変換しなければなりませんでした。このような問題を解決する手段として`useEffect`が利用できます。
`useEffect`では、クラスコンポーネントで利用していたライフサイクルメソッドである`componentDidMount`、`componentDidUpdate`、`componentWillUnmount`を１種類の関数で扱うことができます。

![useEffect.001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/622836/56bfb228-9546-031a-bd0f-9aa0a352879d.png)


またHooksがないときにはクラスコンポーネントと関数コンポーネントが混在していましたが、`useEffect`や`useState`などのHooksの機能を利用することで全てを関数コンポーネントで記述できるため、コード全体の可読性を高めることもできるでしょう。


#useEffectが呼び出されるタイミング

はじめに、関数コンポーネントがレンダリングおよび再レンダリングされるタイミングについて説明します。

**初回レンダリングのタイミング**：

- 関数コンポーネントがJSXとして読み込まれたとき

**再レンダリングのタイミング**：

- 親コンポーネントが再レンダリングされたとき
- 受け取っている`props`に変化が生じたとき
- 同コンポーネント内で`useState`を用いて定義している変数に変化が生じたとき

など

`useEffect`はこのようなレンダリングのタイミングで、自身で指定した任意の変数に変化が生じたときにコールバックとして記述した処理を実行します。


#useEffectの使い方

では実際に`useEffect`の使い方について見ていきましょう。`useEffect`の書き方の違いで呼び出される条件が変化するので、しっかり抑えておきましょう。

###useEffectを利用するための基本
**useEffectのインポート**
何か別のライブラリを読み込む必要はなく、Reactからインポートして使用できます。

```react
import React, { useEffect } from 'react';  // ここでuseEffectをインポートする
```

**useEffectの基本構文**
`useEffect`には第一引数としてコールバック関数、第二引数として任意の変数を格納する配列を記述します。
なお第二引数は省略可能です。

```react
useEffect(() => {
  // 処理
}, [/* 任意の変数 */]);
```
以上が`useEffect`を利用するための基本となります。

次に使用目的ごとに実際の記述方法について見ていきます。

###レンダリングされるタイミングで毎回実行する
初回レンダリング時および再レンダリングに毎回`useEffect`に渡したコールバック関数を実行する方法について説明します。
レンダリング時に毎回実行する方法は、`useEffect`の第二引数を省略することです。

```react:App.js
import React, { useEffect } from 'react';

const App = () => {
  useEffect(() => {
    // 処理
  });  // 第二引数を省略

  return (
    // JSX
  );
};
```
※ ただし第二引数を省略し、レンダリング時に毎回実行するようにすると何らかの原因で無限ループに陥る可能性があるので極力このような記述はしない方がいいでしょう。

###初回レンダリング時のみ実行する
クラスコンポーネントのライフサイクルメソッドの`componentDidMount`のように初回レンダリング時にのみ処理を実行したいときは、第二引数の配列を空にして記述します。

```react:App.js
import React, { useEffect } from 'react';

const App = () => {
  useEffect(() => {
    // 処理
  }, []);  // 第二引数の配列を空にする

  return (
    // JSX
  );
};
```

###任意の変数が変化したときのみ実行する
関数コンポーネント内で使用している変数に変化が生じたときに、`useEffect`に渡したコールバック関数を実行したいときには、第二引数の配列にその変数を指定します。
少し具体的な例として、ボタンを押すと数値が加算されるカウンターを作成してみます。

```react:Counter.js
import React, { useState, useEffect } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    console.log('useEffect!!')
  }, [count]);  // 第二引数の配列に任意の変数を指定（ここではcountが変化した時のみ実行されるようにしている）

  return (
    <div>
      {count}
      <button onClick={() => setCount((prevCount) => prevCount + 1)}>
        ADD 1
      </button>
    </div>
  );
};
```
簡単な例ではありますが、このように記述することで`count`が変化したときにのみ`useEffect`に渡しているコールバック関数が実行されます。

以上が`useEffect`の基本的な使い方です。


#useEffectで非同期処理をする
`useEffect`内の`() => {}`には**非同期関数**または**`promise`**を返す関数を記述することができません。
ではどのように`useEffect`で非同期処理を行えばいいのでしょうか。
結論としては２通りの方法があります。

- **方法１**

　`useEffect`の外で別の関数を定義し、その関数を`useEffect`で呼び出す

- **方法２**

　`async, await`を利用して特殊な記述をする

方法１については何となくイメージができると思いますので、ここでは方法２について詳しい記述方法を紹介します。

```react
import React, { useEffect } from 'react';

const App = () => {

  useEffect(() => {
    (async () => {
      await // 処理
    })();
  }, [/* 任意の変数 */]);

  return (
    // JSX
  );
};
```
少し見慣れない書き方かもしれませんがこのように記述することで、`useEffect`で非同期処理を行うことができます。

誤って、

```react
// BAD example!!
useEffect(async () => {
  await //  処理
}, [/* */])
```
としないように注意しましょう。

#まとめ
簡単ではありますが、`useEffect`についての説明は以上になります。
まとめると、

- `useEffect`は**関数コンポーネントでライフサイクルメソッドを持たせる**ための機能
- クラスコンポーネントで利用していたライフサイクルメソッドを一つの関数で表現できる
- 記述の仕方で**実行タイミングが変わる**
- 非同期処理させるためには注意が必要

といった感じです。

実際には`useEffect`の使い方としてはまだまだたくさんありますが、基本中の基本はこの記事にまとめてあることなのでしっかり抑えていただければと思います。
今後アプリケーションを作成する際に、適した使い方をしていきましょう。

また参考資料に記載しているリンク先ではさらに詳しい使い方や解説をしてくれていますので、是非みて見てください。

#参考資料

- [useEffect / React Hooks – React入門](https://www.to-r.net/media/react-tutorial-hooks-useeffect/)
- [useEffectフックのしくみ](https://qiita.com/ossan-engineer/items/740425a0df937a47e093)
- [React.useEffectで非同期処理をする場合の注意点2つ](https://qiita.com/daishi/items/4423878a1cd7a0ab69eb)