基礎から学ぶReact/React Hooks学習メモ 5-2 useEffect

# React Hooksを基礎から理解する 5-2 useEffect

参考
- [基礎から学ぶReact/React Hooks](https://www.amazon.co.jp/dp/B09CP8T4TX)

## useEffect

- useEffect()に渡された関数の実行タイミングを、コンポーネントのマウント後、アンマウント後、または更新後まで遅らせることができるフック。
- 副作用とは、Reactの管理外であるDOMを更新する処理やAPIとの非同期通信等のデータ処理、購読（subscription）の設定など、UI構築以外の処理を指す。
- useEffect()により副作用の実行タイミングが遅延され「UI構築後に行われる処理」と考えていい。
- useEffect()に渡されたコールバック関数は、描画完了のタイミングで実行されるので、正しく構築されたDOMを参照することができる。無限ループに陥ることなく、安全にDOMを参照できる。

### インポート

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

### 基本構文

- 第2引数を省略すると、コンポーネントがレンダリングされるたびに副作用関数が実行されるため、無限ループに陥りやすく、第2引数を省略することはほとんどない。

```js
useEffect(副作用関数, [依存する変数の配列])
```

- 初回レンダリング時のみ副作用関数を実行させる

```js
useEffect(() => console.log("副作用関数が実行されました"), []);
```

- 依存関数の要素が変化した場合のみ副作用関数を実行させる

```js
useEffect(() => document.title = `${userName} ${frameworkName}`, [userName, frameworkName]);
```

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

- useEffect()の副作用関数から返された関数を、クリーンアップ関数という。
- クリーンアップとは、タイマーのキャンセル処理、イベントリスナの削除など。
- レンダリングするたびにイベントが重複してしまうので、マウント時に実行した処理をアンマウント時に削除する処理が必要。

```js
useEffect(() => {
  console.log("副作用関数が実行されました！");

  return () => {
    alert("クリーンアップ関数が実行されました！");
  }
}, [])
```

### useEffectの利用例

- クリーンアップを必要としないコード例
    - ボタンをクリックするとカウンターが加算、リセットされ、ブラウザのタイトルも同時に変更される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/7f018cbc-f84d-61b9-a3b2-d5ff9d90845a.png)


```js
import React, { useState, useEffect } from "react";
import "./styles.css";

const INITIAL_COUNT = 0;

const SampleComponent = () => {
  const [count, setCount] = useState(INITIAL_COUNT);

  // countが変更されたときに、タイトルを変える
  useEffect(() => {
    document.title = `${count} 回クリックされました`;
  }, [count]);

  const countIncrement = () => {
    setCount((prevCount) => prevCount + 1);
  };

  const countReset = () => {
    setCount(INITIAL_COUNT);
  };

  return (
    <div className="App">
      <p>現在のカウント数：{count}</p>
      <button onClick={countIncrement}>+1ボタン</button>
      <button onClick={countReset}>リセット</button>
    </div>
  );
};

export default function App() {
  return <SampleComponent />;
}
```

- クリーンアップを必要とするコード例
    - アンマウントまたは副作用が再実行されたときに、クリーンアップ関数が実行される
    - 「タイマーを表示」ボタンをクリックするとタイマーが表示され、useEffect()の副作用関数が実行される
    - 「タイマーを非表示」ボタンをクリックするとタイマーが非表示になり、useEffect()の副作用関数のクリーンアップ関数が実行される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/ed67baaf-cd5a-7c27-1985-0c828947a4a6.png)


```js
import React, { useState, useEffect } from "react";
import "./styles.css";

const INITIAL_COUNT = 0;

const Timer = () => {
  const [count, setCount] = useState(INITIAL_COUNT);

  const countReset = () => {
    setCount(INITIAL_COUNT);
  };

  const countIncrement = () => {
    setCount((prevCount) => prevCount + 1);
  };

  // 副作用関数が初回レンダリング時に実行される
  useEffect(() => {
    console.log("副作用関数が実行されました！");
    const timer = setInterval(countIncrement, 1000);
    return () => {
      // アンマウント時に実行される
      console.log("timerが削除されました！");
      clearInterval(timer);
    };
  }, []);

  return (
    <div className="App">
      <p>現在のカウント数：{count}</p>
      <button onClick={countReset}>RESET</button>
    </div>
  );
};

export default function App() {
  const [display, toggleDisplay] = useState(false);

  // ボタンによって、Timerコンポーネントの表示、非表示が行われる
  const handleToggleDisplay = () => {
    toggleDisplay(!display);
  };

  return (
    <>
      <button onClick={handleToggleDisplay}>
        {display ? "タイマーを非表示" : "タイマーを表示"}
      </button>
      {display && <Timer />}
    </>
  );
}
```