useEffect を書きながら学ぶ（React Hooks 入門シリーズ 5/6）

## ロードマップ

React 16.8 で追加された機能であるReactの`Hooks`について書いてあります。

書きながら学ぶ React Hooks 入門シリーズとして書き下ろしました。

- [React.memo / useCallback / useMemo](https://qiita.com/ryosuketter/items/c503d98e16c28f516c4d)
- [Context / useContext](https://qiita.com/ryosuketter/items/90bd9edd5d91b8daf5c9)
- [useRef](https://qiita.com/ryosuketter/items/f2cc554cc89fb41d5a0b)
- [useState](https://qiita.com/ryosuketter/items/40dfc139ed90ebffd284)
- [useEffect](https://qiita.com/ryosuketter/items/6551ee0d75a3da003980) :point_left_tone2: 今回
- [useReducer](https://qiita.com/ryosuketter/items/3d59f1c503fe27fa0cb9)

## はじめに

Reactの組み込みフックであると`useRef`の説明をします。

## useEffect とは

コンポーネントに副作用を追加するフックです。

副作用とは以下の4つです。

- Reactから生成されたDOMの変更
- APIとの通信
- 非同期処理
- console.log

副作用が分かりにくいので、もっと噛み砕いて言えば

`useEffect`は、コンポネントのレンダリング後か、アンマウント（コンポネントをDOMから削除）後に、なにかの処理を実行させたいときに使うといいやつ。

って覚えとけばいいと思います。

### 構文

```js
useEffect(副作用, [deps])
```

引数の渡し方によって、副作用が実行されるタイミングは異なります。

#### 引数の渡し方によって、副作用が実行されるタイミング...1（毎回）

これもちゃんと動作します。

```js
useEffect(副作用)
```

これは毎回のレンダリング後に副作用が実行されます。

#### 引数の渡し方によって、副作用が実行されるタイミング...2（[deps]更新時）

これは、例えば カウントアプリでボタンクリック時に `state` （count） が更新された時、useEffect 内が実行されます。

```js
useEffect(()=>{
  setCount((count) => count + 1)
}, [count])
```

#### 引数の渡し方によって、副作用が実行されるタイミング...3（最初の1回だけ）

第1引数をからの配列にすると、コンポネントがレンダリングされた後に1回だけ実行されます。

```js
useEffect(()=>{
  console.log('completed render')
}, [])
```

#### クリーンナップ関数

クリーンナップ関数は以下のタイミングで実行されます。

- コンポネントのアンマウント時
- 副作用の再実行時

```js
useEffect(() => {
  console.log('completed render');
  const cleanup = () => {
    console.log('cleanup!');
  };
  return cleanup;
}, []);
```

## 活用例

useEffectを使って、外部APIと通信し、非同期処理をさせて表示させましょう。

![4c7bb7b56416740fee452196b0261df1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/b149f948-c23c-9989-d30e-06a3f7052bd2.gif)

APIは、ダミーの顔写真付きユーザー情報をJSONで取得できるする「Random User Generator」を使います。

https://randomuser.me/

### 最初の1回だけしかレンダリングされない バージョン

![4c7bb7b56416740fee452196b0261df1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/b149f948-c23c-9989-d30e-06a3f7052bd2.gif)

<details><summary>サンプルコード</summary><div>

```js
import React, { useState, useEffect } from "react";

const fetchUser = async () => {
  const response = await fetch("https://api.randomuser.me/");
  const data = await response.json();
  const [user] = data.results;
  return user;
};

const App = () => {
  const [user, setUser] = useState({});
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUser().then((userData) => {
      setUser(userData);
      setLoading(false);
    });
  }, []);

  return (
    <div className="App">
      {loading ? (
        <p>loading...</p>
      ) : (
        <>
          <p>
            {user.name.first} {user.name.last} from: {user.location.country}
          </p>
          <img src={user.picture.large} alt={user.name.first} />
        </>
      )}
    </div>
  );
};

export default App;
```

</div></details>

### useEffectの依存配列に何か指定したバージョン

依存配列に `count` state を指定した場合

これは、`Next User`ボタンクリック時に state （count） が更新されたことをフックして、useEffect 内が再度実行されます。

![abebf6d886b8fdcce50544e032408c57.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/16502da7-fae1-0a52-c0b2-a16241dbd265.gif)

<details><summary>サンプルコード</summary><div>

```js
import React, { useState, useEffect } from "react";

const fetchUser = async () => {
  const response = await fetch("https://api.randomuser.me/");
  const data = await response.json();
  const [user] = data.results;
  return user;
};

const App = () => {
  const [user, setUser] = useState({});
  const [loading, setLoading] = useState(true);
  const [count, setCount] = useState(1);

  useEffect(() => {
    fetchUser().then((userData) => {
      setUser(userData);
      setLoading(false);
    });
  }, [count]);

  const updateUser = () => setCount((currentCount) => currentCount + 1);

  return (
    <div className="App">
      {loading ? (
        <p>loading...</p>
      ) : (
        <>
          <p>Entry Number: {count}</p>
          <p>
            {user.name.first} {user.name.last} from: {user.location.country}
          </p>
          <img src={user.picture.large} alt={user.name.first} />
        </>
      )}
      <div>
        <button onClick={updateUser}>Next User</button>
      </div>
    </div>
  );
};

export default App;
```

</div></details>

### useEffectの依存配列に何も指定しないバージョン

まず以下は無限ループを繰り返す危険なコードです。

下記のサンプルコードの場合、APIをリクエストして、`user`stateが書き換わったことをトリガーにして、またAPIをリクエストして、`user`stateが書き換わることを繰り返します。

<details><summary>サンプルコード</summary><div>

```js
import React, { useState, useEffect } from "react";

const fetchUser = async () => {
  const response = await fetch("https://api.randomuser.me/");
  const data = await response.json();
  const [user] = data.results;
  return user;
};

const App = () => {
  const [user, setUser] = useState({});
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUser().then((userData) => {
      setUser(userData);
      setLoading(false);
    });
  });

  return (
    <div className="App">
      {loading ? (
        <p>loading...</p>
      ) : (
        <>
          <p>
            {user.name.first} {user.name.last} from: {user.location.country}
          </p>
          <img src={user.picture.large} alt={user.name.first} />
        </>
      )}
    </div>
  );
};

export default App;
```

</div></details>

## さいごに

`useEffect` にはまだまだ奥が深い部分があります。もっと詳細に書こうと思いましたが、別の機会にします。
詳しくは、以下の記事を参考にしてください。

- [useEffect完全ガイド — Overreacted](https://overreacted.io/ja/a-complete-guide-to-useeffect/)
- [副作用フックの利用法 – React](https://ja.reactjs.org/docs/hooks-effect.html)

僕もまた書きます。

今回は以上です。

## 参考

https://ja.reactjs.org/tutorial/tutorial.html