2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

副作用管理にuseEffectを利用する理由

Last updated at Posted at 2025-03-01

useEffect の役割

useEffect は、「コンポーネントのレンダリング」と「副作用の実行」を分離するためのフック です。

主に以下の目的で使用されます。

** useEffect の目的**

  1. 副作用の適切な実行タイミングを制御する
    • 初回マウント時のみ実行([] 依存配列)
    • 依存関係の変更時に実行([dependency]
    • クリーンアップ処理を適用(return でリソース解放)
  2. 不要な副作用の実行を防ぐ
    • 過剰な再レンダリングを避ける(依存配列の適切な管理)
    • 不要なデータ取得を抑制(前回のリクエストをキャンセルするなど)

副作用(Side Effects)とは?

副作用(Side Effect)」とは、コンポーネントの関数実行(レンダリング)とは直接関係がない処理 を指します。

副作用は、コンポーネント外の状態を変更する操作 を含むため、純粋関数では扱えません

** 副作用の具体例**

  • データ取得(API コール)
  • イベントリスナーの登録・解除
  • 外部の状態(localStorage / sessionStorage など)を変更
  • DOM の直接操作

これらの副作用を適切に管理するために、useEffect を活用し、必要なタイミングで実行・クリーンアップを行うことが重要 です。

なぜ副作用の管理には useEffect が用いられるのか?

① レンダリングを純粋な UI 計算処理として保ち、副作用を分離する

なぜコンポーネントのレンダリングを純粋な UI 計算処理として保つことが重要なのか?

  1. 副作用の実行が必要なタイミングは、レンダリングのタイミングとは必ずしも一致しない
    • データ取得は、コンポーネントが描画されるたびに実行する必要はなく、初回マウント時のみで十分なケースが多い

    • 例えば、API からデータを取得する場合:

      useEffect(() => {
        fetch("/api/posts")
          .then((res) => res.json())
          .then((data) => setPosts(data));
      }, []); // 空の依存配列を指定することで、初回マウント時のみ実
      
    • もし useEffect を使わず、レンダリング時に直接データを取得すると、レンダリングごとに API リクエストが発生し、無駄な負荷がかかる

  2. 不要な再レンダリングを防ぐ
    • データ取得の結果を setState で保存する場合、その state の変更が 再レンダリングを引き起こす

    • もしデータ取得がレンダリングのたびに行われると、無限ループのように再レンダリングが繰り返されてしまう

      
      const [posts, setPosts] = useState([]);
      
      // NG: 直接データ取得を行うと、レンダリングのたびに実行される
      const data = fetch("/api/posts").then((res) => res.json());
      setPosts(data);
      
      
    • useEffect を使えば、依存配列を適切に設定することで 不要な副作用の再実行を防ぐ ことができる。

② UI の予測可能性を高める

  • React は「状態 (state) と入力 (props) に基づいて UI を決定する」という原則を持つ

  • 副作用が適切に管理されていないと、状態がどのように変化するか予測が難しくなり、デバッグが困難になる

  • 例えば、コンポーネントのマウント時に API からデータを取得し、そのデータに基づいて state を更新する場合

    
    useEffect(() => {
      fetch("/api/posts")
        .then((res) => res.json())
        .then((data) => setPosts(data));
    }, []);
    
  • useEffect を使わずにこの処理をコンポーネントの中で直接書いてしまうと、

    • どのタイミングで API コールが実行されるのか不明確
    • データが取得できる前の state の状態が分かりづらくなる
    • 予期せぬ再レンダリングが発生する
  • そのため、useEffect に副作用を分離することで、「このコンポーネントは state の変更によって UI を更新するだけ」 というシンプルな構造を保つことができる。


③ パフォーマンス最適化のため

  • 副作用が適切に管理されないと、レンダリングのたびに不要な処理が実行、関数の再生成が起こり、パフォーマンスが低下する

  • 特に API コールやイベントリスナーの登録 などは、無駄に実行されるとアプリのレスポンスが遅くなる。

  • useEffect を使えば、以下のように 依存配列を適切に設定することで最適なタイミングで実行可能

    
    useEffect(() => {
      window.addEventListener("resize", handleResize);
    
      return () => {
        window.removeEventListener("resize", handleResize); // クリーンアップ
      };
    }, []); // 初回マウント時のみ登録
    
  • これにより、不要なイベントリスナーの登録・解除が防がれ、メモリリークのリスクも回避できる

useEffect 内で setState を行う場合の React のレンダリングとコミットの流れ(初回マウント時のみ useEffect 実行するケース)

  1. 初回のレンダリング

    • コンポーネントが初回レンダリングされ、仮の UI が描画される。
    • この時点では useEffect はまだ実行されない(レンダリング後に実行されるため)。
  2. 初回コミット

    • 初期 UI がブラウザに描画され、DOM に反映される。
    • ここで useEffect が実行される
  3. useEffect 内で setState が発生

    • useEffect 内で setState を実行すると、新しい状態が適用され、再レンダリングがトリガーされる
  4. 再レンダリング

    • setState によって変更された状態を反映し、React がコンポーネントを再レンダリングする。
  5. 再コミット

    • 変更後の UI が DOM に適用される。
    • useEffect は依存配列 [] のため再度実行されない(初回マウント時のみ実行)。

グローバル状態の更新は副作用に挙げられるか?

  Yes、グローバル状態の 「更新」 は副作用とみなされる。

理由

  1. 「取得」自体も時に副作用である

    • グローバル状態(例: Redux / Context API / Zustand など)を取得するだけでは UI の計算に影響を与えない場合が多いが、
      「取得時に API からデータをフェッチする」場合は明らかに副作用となる
  2. 「更新」は副作用として管理すべき

    • useEffect を使わずに setGlobalState をコンポーネントのレンダリング時に実行すると、
      レンダリングのたびに状態が更新され、不要な再レンダリングが発生する可能性がある
  3. レンダリングとグローバル状態の更新を同時に行うと「UI の計算結果がブレる」

    • 状態の変更が「いつ適用されるか」 が不明確になり、意図しない UI の不整合が発生するリスクがある

マウント時にグローバル状態を扱いたいケースでの適切な管理方法

 グローバル状態の「取得」useSelector()useContext() などを使い、レンダリング時に取得
 グローバル状態の「更新」useEffect() + dispatchや mutation を使って副作用として実行(マウント時にそもそも更新するケースはあまりないかもしれないが、、、)

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?