ReactのuseEffectについて

Reactの`useEffect`は、**副作用を扱うためのフック**です。
「副作用」とは、**データの取得・DOMの操作・イベントリスナーの登録・タイマーの設定**など、**Reactのレンダリングの外で起こる処理**のことです。
ちなみに、「Reactのレンダリングの外」とは、**コンポーネントが画面に表示される過程（＝レンダリング）とは直接関係のない処理**のことです。


###  基本的な使い方

```tsx
useEffect(() => {
  // 副作用の処理（例：APIリクエスト、console.log など）
}, []);
```

### 引数の意味

```tsx
useEffect(() => {
  // ① 副作用の処理を書く関数（必須）

  return () => {
    // ② クリーンアップ関数（任意）
  };
}, [依存配列]);
```

* **第1引数**：副作用の関数
* **第2引数（依存配列）**：

  * 空配列 `[]` → **初回マウント時のみ実行**
  * 配列なし → **レンダリングのたびに実行**
  * 配列に値あり → **その値が変化したときに実行**

---

## 典型的なユースケース
### ① 初回マウント時のみ実行（初期化処理）

#### 例：ページ読み込み時にデータを取得

```tsx
useEffect(() => {
  fetch("/api/user").then((res) => res.json()).then(setUser);
}, []); // 空配列 → 初回だけ実行
```

使い道：初回だけ実行したい処理（API呼び出し・初期設定）


### ② 値の変化に応じて実行（依存配列あり）

#### 例：選択したIDに応じてデータ再取得

```tsx
useEffect(() => {
  fetch(`/api/user/${userId}`).then((res) => res.json()).then(setUser);
}, [userId]); // userId が変わるたびに実行
```

使い道：フォーム入力や選択値に応じた処理、フィルターの変更など

### ③ タイマー処理

#### 例：5秒後にフラグを更新

```tsx
useEffect(() => {
  const timer = setTimeout(() => {
    setIsVisible(false);
  }, 5000);

  return () => clearTimeout(timer); // クリーンアップ
}, []);
```

使い道：トースト通知の消去、遅延処理、非同期処理のシミュレートなど


### ④ フォーム入力のリアルタイムバリデーション

#### 例：メールアドレスのバリデーションチェック

```tsx
useEffect(() => {
  if (email.includes("@")) {
    setEmailValid(true);
  } else {
    setEmailValid(false);
  }
}, [email]);
```

使い道：入力補助やボタンの活性化制御など

### ⑤ ローカルストレージとの連携
#### 例：ブラウザの設定を維持
```tsx
useEffect(() => {
  const saved = localStorage.getItem("theme");
  if (saved) {
    setTheme(saved);
  }
}, []);

useEffect(() => {
  localStorage.setItem("theme", theme);
}, [theme]);
```

使い道：ダークモードやユーザー設定の保存・復元

---

## よくあるミスや注意点
### ① 依存配列（第二引数）を省略する

```tsx
useEffect(() => {
  fetch("/api/data").then(setData);
}); //  依存配列なし
```

問題点：レンダリングごとに毎回実行されて、無限ループや通信のしすぎの原因に


正しくは：

```tsx
useEffect(() => {
  fetch("/api/data").then(setData);
}, []); // 初回のみ実行
```

### ② 不適切な依存配列（必要な値を入れ忘れる）

```tsx
useEffect(() => {
  console.log(user.name); // userが変わっても再実行されない
}, []); //  userが依存配列にない
```

問題点：

* 値は変わっているのに副作用が実行されない
* 古い値を使ってしまう

正しくは：

```tsx
useEffect(() => {
  console.log(user.name);
}, [user]); // userが変わったら再実行
```

### ③ 非同期関数を直接使う

```tsx
useEffect(async () => {
  const res = await fetch("/api");
  const data = await res.json();
  setData(data);
}, []); // useEffectにasyncは直接書けない
```

問題点： `useEffect` は `async` を返すとエラーになる
**useEffect の返り値は、クリーンアップ関数（または undefined）でなければならないです。**

正しくは：

```tsx
useEffect(() => {
  const fetchData = async () => {
    const res = await fetch("/api");
    const data = await res.json();
    setData(data);
  };
  fetchData();
}, []);
```

### ④ クリーンアップを忘れる

```tsx
useEffect(() => {
  window.addEventListener("resize", handleResize);
}, []); // クリーンアップがない
```

問題点：

* イベントがどんどん登録されて「ゴーストイベント」になる
* メモリリークの原因にもなる

正しくは：

```tsx
useEffect(() => {
  window.addEventListener("resize", handleResize);
  return () => {
    window.removeEventListener("resize", handleResize); // ✅ クリーンアップ
  };
}, []);
```

### ⑤ 状態を連続で更新 → 無限ループ

```tsx
useEffect(() => {
  setCount(count + 1); // useEffectで状態を変えると再実行され続ける
}, [count]);
```

問題点：

* `count` を変えるたびに `useEffect` が実行される
* `setCount` → countが変わる → useEffect再実行 → 無限ループ

対策：

* 条件をつける、または `setTimeout` などで制御する
* `useRef` を使って初回だけスキップするなど

---

## useEffectを使わない方がよいケース

### 1. **状態に応じた処理が「そのままレンダリングに使える」とき**

#### NG例（useEffectでバリデーションをしている）

```tsx
const [email, setEmail] = useState("");
const [isValid, setIsValid] = useState(false);

useEffect(() => {
  setIsValid(email.includes("@"));
}, [email]);
```

#### よりシンプルな書き方（useEffect不要）

```tsx
const isValid = email.includes("@");
```

 状態 `email` から導かれる値 `isValid` は、
計算で求まるので useEffect ではなく「変数」または `useMemo` で十分です。


### 2. **状態を更新するだけのロジックをuseEffectに書いてしまう**

#### NG例（ボタンを押したらカウントを上げる → useEffectで状態更新）

```tsx
const [count, setCount] = useState(0);
const [clicked, setClicked] = useState(false);

useEffect(() => {
  if (clicked) setCount((prev) => prev + 1);
}, [clicked]);
```

#### より適切な書き方（クリック時に直接更新）

```tsx
const [count, setCount] = useState(0);

const handleClick = () => {
  setCount((prev) => prev + 1);
};
```

「ボタンを押したら実行」→ これはイベントハンドラで処理すべきで、副作用ではない。

### 3. **ユーザー入力の状態をリアルタイムに保持するだけのとき**

#### NG例（入力を状態に反映するだけなのに useEffect）

```tsx
const [input, setInput] = useState("");
const [text, setText] = useState("");

useEffect(() => {
  setText(input);
}, [input]);
```

#### 正しくは `text` 状態そのものが不要

```tsx
const [text, setText] = useState(""); // 入力と表示が同じならこれ1つでOK
```

「ある状態が別の状態にコピーされるだけ」なら **そもそも状態を分けない方が良い**。

### 4. **他のReactフックで代替できるとき**

| やりたいこと           | 適した手法         | なぜ useEffect は不要？ |
| ---------------- | ------------- | ----------------- |
| 計算コストの高い処理の結果を保持 | `useMemo`     | レンダリング時に再計算されない   |
| イベント内の関数を再利用     | `useCallback` | 無駄な再生成を防げる        |
| 状態管理が複雑          | `useReducer`  | 状態とロジックの分離ができる    |

#### 無駄な`useEffect`が増えることのデメリット
* 可読性・保守性の低下
* テストやデバッグが困難
* 責務の分離ができなくなる
* パフォーマンス低下

#### seEffectを適切に使うためには
* JSXだけでできることはuseEffectに書かない
* 状態の変化によって「Reactの外側」で何かするならuseEffect
* 副作用は**開始と終了**をワンセットで考える
* 「これuseEffect必要？」と**1回立ち止まって考える**

## まとめ
`useEffect` は便利に見えるが「とりあえず書く」のではなく、**使うべきタイミングと使わなくていい場面を見極めること**が大切。
Reactの「状態の変化で自動的に再描画される」という特徴を活かしながら、`useEffect` を**最小限・適切に**使えるようになれば、よりシンプルでバグの少ない設計が実現できる。