【React】useEffectの使い方を分かりやすく解説!

この記事ではReactフックの1つである『useEffect』について、

  • useEffectとは
  • useEffectの構文
  • useEffectの使い方
    • 依存配列に変数を指定した場合の使い方
    • 依存配列に空の依存配列[]を指定した場合の使い方
    • 依存配列を省略した場合の使い方
    • クリーンアップ関数の使い方

などをサンプルコードを用いて分かりやすく説明するように心掛けています。ご参考になれば幸いです。

useEffectとは

useEffectは、副作用を管理するために使用されるReactのフックの一つです。useEffectの第1引数に渡された関数は、コンポーネントがレンダリングされた後やuseEffectの第2引数に指定した変数の変化時に実行されます。

useEffectは副作用の処理(例:API通信、変数代入、関数実行、手動でのDOM操作など)を関数コンポーネントで適切に管理するために使用します。

補足

  • useEffectはReact16.8で導入されたフックです。

useEffectの構文

useEffectは2つの引数を取ります。以下にその構文を示します。第1引数は副作用関数、第2引数は依存配列です。

useEffectの構文

useEffect(sideEffectsFn, [dependencies]);
  • sideEffectsFn: 副作用関数
  • dependencies(省略可能): 依存配列

useEffectは第2引数の依存配列によって、主に3つのパターンがあります。

1. 依存配列に変数を指定した場合

useEffect(() => {
  // 初回のレンダリング時と変数(hoge, foo)が変化した時のみ実行される
},[hoge, foo]); // 依存配列に変数(hoge, foo)を指定

依存配列に変数を指定した場合、コンポーネントの初回のレンダリング時とその変数が変化した時のみ副作用関数が実行されます。

2. 依存配列に空の依存配列[]を指定した場合

useEffect(() => {
  // 初回のレンダリング時のみ実行される
},[]); // 依存配列に空の配列[]を指定

依存配列に空の配列[]を指定した場合、コンポーネントの初回のレンダリング時のみ副作用関数が実行されます。

3. 依存配列を省略した場合

useEffect(() => {
  // レンダリング毎に実行される
}); // 依存配列を省略

依存配列を省略した場合、コンポーネントのレンダリング毎に副作用関数が実行されます。

このように、第2引数の依存配列を指定することにより、第1引数の副作用関数の実行タイミングを制御することができます。

では、それぞれのパターンにおいて、実際にサンプルコードで動作を確認してみましょう。

useEffectの使い方

useEffectについて、以下に示している使い方をこれから説明します。

  • 依存配列に変数を指定した場合の使い方
  • 依存配列に空の依存配列[]を指定した場合の使い方
  • 依存配列を省略した場合の使い方
  • クリーンアップ関数の使い方

依存配列に変数を指定した場合の使い方

依存配列に変数を指定した場合、コンポーネントの初回のレンダリング時とその変数が変化した時のみ副作用関数が実行されます。

以下のサンプルコードでは、依存配列には変数count1のみを指定しています。そのため、変数count1が変化した時に副作用関数が実行されますが、変数count2が変化した時には副作用関数が実行されません。

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

const App = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  // 変数 count1 が変化した時に副作用関数が実行される
  useEffect(() => {
    console.log(`useEffect: count1 = ${count1}`);
  }, [count1]); // 依存配列には変数 count1 のみを指定

  return (
    <div>
      <p>Count1: {count1}</p>
      <button onClick={() => setCount1(count1 + 1)}>Increment Count1</button>
      <p>Count2: {count2}</p>
      <button onClick={() => setCount2(count2 + 1)}>Increment Count2</button>
    </div>
  );
};

export default App;

依存配列に空の依存配列[]を指定した場合の使い方

依存配列に空の配列[]を指定した場合、コンポーネントの初回のレンダリング時のみ副作用関数が実行されます。

以下のサンプルコードでは、変数count1や変数count2が変化すると再レンダリングが発生しますが、副作用関数が実行されません。

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

const App = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  useEffect(() => {
    console.log('useEffect: 初回レンダリング時にのみ実行');
  }, []); //依存配列に空の配列[]を指定している

  return (
    <div>
      <p>Count1: {count1}</p>
      <button onClick={() => setCount1(count1 + 1)}>Increment Count1</button>
      <p>Count2: {count2}</p>
      <button onClick={() => setCount2(count2 + 1)}>Increment Count2</button>
    </div>
  );
};

export default App;

依存配列を省略した場合の使い方

依存配列を省略した場合、コンポーネントのレンダリング毎に副作用関数が実行されます。

以下のサンプルコードでは、変数count1count2が変化すると、再レンダリングが発生し、そのたびに副作用関数が実行されます。

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

const App = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  useEffect(() => {
    console.log('useEffect: レンダリング毎に実行');
  }); // 依存配列を省略している

  return (
    <div>
      <p>Count1: {count1}</p>
      <button onClick={() => setCount1(count1 + 1)}>Increment Count1</button>
      <p>Count2: {count2}</p>
      <button onClick={() => setCount2(count2 + 1)}>Increment Count2</button>
    </div>
  );
};

export default App;

実際には、第2引数を省略するケースはほとんどありませんが、第2引数を省略すると、コンポーネントのレンダリング毎に副作用関数が実行されるため、無限ループの原因になることがあります。無限ループしているサンプルコードを以下に示します。

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

const App = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('useEffect: 無限ループ');
    setCount(count + 1);
  });

  return (
    <div>
      <p>Count: {count}</p>
    </div>
  );
};

export default App;

この例では、副作用関数内の処理でsetCountが実行され、状態が変化するので、再レンダリングが発生します。再レンダリングが発生すると再度、副作用関数内の処理でsetCountが実行されてしまうため、無限ループになります。

クリーンアップ関数の使い方

useEffectの第1引数に渡された副作用関数はクリーンアップ関数を返すことができます。クリーンアップ関数は「コンポーネントがアンマウントされる前」と「useEffect の依存配列に指定された値が変わり、エフェクトが再実行される前」に実行されます。

  • マウント:コンポーネントが既存のDOMに適用、追加されること
  • アンマウント:コンポーネントがDOMから削除されること

コンポーネントがアンマウントされるのは、例えば、条件分岐でコンポーネントの表示非表示をしており、ある条件でコンポーネントが非表示になった時です。以下のサンプルコードでは、変数countが奇数になった時に、ChildComponentが非表示になり、そのタイミングでクリーンアップ関数が実行されます。

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

const ChildComponent = () => {
  useEffect(() => {
    console.log('ChildComponent: マウントされました');
    return () => {
      console.log('ChildComponent: アンマウントされました');
    };
  }, []);

  return <div>Child Component</div>;
};

const App = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      {count % 2 === 0 && <ChildComponent />}
    </div>
  );
};

export default App;

クリーンアップ関数を用いると、2度目のコンポーネントのレンダリング時に前回の副作用を消すことができます。クリーンアップ関数を返すケースはあまりありませんが、イベントを登録している場合や、非同期通信をsubscribeしている場合などにクリーンアップ関数でそれらを解除します。

以下のサンプルコードでは、ChildComponent内の副作用関数でウィンドウのリサイズ時にコンソールにメッセージを表示するイベントリスナーを登録しています。クリーンアップをしないとコンポーネントがレンダリングされるたびにイベントが重複して登録されてしまいます。そのため、クリーンアップ関数で登録したイベントリスナーを解除することで、コンポーネントがアンマウントされた後も不要なイベントリスナーが残らないようにしています。

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

const ChildComponent = () => {
  useEffect(() => {
    const handleResize = () => {
      console.log('ウィンドウサイズが変更されました');
    };

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
      console.log('クリーンアップ: イベントリスナーを解除しました');
    };
  }, []);

  return <div>Child Component</div>;
};

const App = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      {count % 2 === 0 && <ChildComponent />}
    </div>
  );
};

export default App;

本記事のまとめ

この記事ではReactフックの1つである『useEffect』について、以下の内容を説明しました。

  • useEffectとは
  • useEffectの構文
  • useEffectの使い方
    • 依存配列に変数を指定した場合の使い方
    • 依存配列に空の依存配列[]を指定した場合の使い方
    • 依存配列を省略した場合の使い方
    • クリーンアップ関数の使い方

お読み頂きありがとうございました。