当サイトは、アフィリエイト広告を利用しています

【ReactHooks】useEffectとライフサイクル

作成日:2022月03月07日
更新日:2025年08月22日

useEffectについて書く前に、reactコンポーネントのライフサイクルと
ライフサイクルメソッドについてまとめる。

ライフサイクルとは?

ライフサイクルとは、ざっくりいうとReactのコンポーネントが生まれてから成長し、
最終的に死ぬまでの一連の過程のこと。 reactコンポーネントのライフサイクルには大きく3つの期間

  • Mounting
  • Updating
  • Unmounting に分けられる。下記のようなイメージ。

Mounting

  • コンポーネントが生成される
    • コンポーネントが作られレンダリングされるまでの期間

Updating

  • コンポーネントが更新される
    • コンポーネントの管理するデータがユーザによって更新される期間

Unmounting

  • コンポーネントが削除される
    • コンポーネントが不要となり破棄するための期間

ライフサイクルメソッドとは

ライフサイクルの3つの期間(Mounting・Updating・Unmounting)の
それぞれ期間で使用できるメソッドをライフサイクルメソッドという。

ライフサイクルメソッドの実装例

クラスコンポーネントで書いた場合のサンプルを記述する
ただ今後、公式でもクラスコンポーネントではなく関数コンポーネントを推奨しているので
書くことはなくなってくると思う。

Mounting期間で使えるライフサイクルメソッド

componentDidMount()

一番始めのレンダーされた直後に一度だけコールされるメソッド。  

jsx
componentDidMount() {
console.log("componentDidMount")
}

Updating期間で使えるライフサイクルメソッド

componentDidUpdate

コンポーネントが表示された後、ユーザーによる操作(クリック、スクロールなど)によって
再レンダーされた直後にコールされるメソッド。

jsx
componentDidUpdate() {
console.log("componentDidUpdate")
}

Unmounting期間で使えるライフサイクルメソッド

componentWillUnmount

画面遷移などして、コンポーネントが破棄される前にコールされるメソッドです

jsx
componentWillUnmount() {
console.log("componentDidMount")
}

参考

Reactの基礎【ライフサイクル】
【React Hooks】ライフサイクルについて
Reactのライフサイクルメソッドについて

useEffectとは

useEffectとは関数コンポーネントにライフサイクルメソッドを持たせるための機能みたいなもの。
ライフサイクルメソッドは以前はクラスコンポーネントでしか記述できなかったが
今ではuseEffectを使えば、同様の動きを関数コンポーネントで実装することができる

useEffectなどのReactのフックを使う時は、正しく動作させるために

  • フックのルール(Rules of Hooks)

を順守する必要がある。
詳しくは下記でまとめている

useEffectの定義

useEffectの基本構文

  • 任意の処理
  • 任意のクリーンアップ処理 を書くことができる
jsx
useEffect(() => {
// 任意の処理
// 任意のクリーンアップ処理
}, [/* 任意の変数 */]);

第一引数はコールバック関数

第一引数にはコールバック関数を設定し、これはレンダリング後に
動作する。

クリーンアップ処理なし

jsx
useEffect(() => {
// 任意の処理
console.log(`count${count}`);
});

第一引数にクリーンアップ処理を書く

コールバック関数でreturnで関数を返すことでそれをクリーンアップの処理と みなすことができる。 クリーン処理の実行タイミングも第二引数で決まる。

クリーンアップ処理あり

jsx
useEffect(() => {
// 任意の処理
console.log(`count${count}`);
// 任意のクリーンアップ処理
return ()=>{
console.log(`cleanUp`);
}
},[count]);

クリーンアップ処理もcountの値がかわったタイミングで実行される。
処理の実行順としては下記になる

  1. countの値を更新する
  2. countの値が更新されたためコンポーネントが再レンダリングされる
  3. クリーンアップ処理が実行される
  4. 任意の処理が実行される。 ※初期表示時はクリーンアップ処理は動かない

第二引数は任意の変数を格納する配列

第二引数は副作用処理(第一引数の関数)をどのタイミングで実行するかを
決めることができる。
※どのタイミングでもレンダリング後に実行される

第二引数の配列には下記を設定することができる

設定しない※[]自体を書かない

レンダリング後に毎回実行される

jsx
useEffect(() => {
console.log(`count${count}`);
});

空配列

初期マウント時(レンダリング後)に1回のみ実行される

jsx
useEffect(() => {
console.log(`count${count}`);
},[]);

配列に任意のstateを設定する。

countが変わり、画面が再レンダリングされた後に実行される

jsx
useEffect(() => {
console.log(`count${count}`);
},[count]);

useEffectの実行サンプル

useEffectの実行パターンサンプルをいくつか下記に記載する。
下記のただカウントするだけのcounter.jsxをuseEffectで書き換えていってみる

jsx
import "./styles.css";
import React, { useState } from "react";
export default function App() {
//useState
const [count,setCount] = useState(0)
//カウントアップ
const countUp = ()=>{
setCount((prevCount)=>prevCount+1)
}
return (
<div className="App">
<div>
<h2>カウンター</h2>
<button onClick={countUp} >click me</button>
{count}
</div>
</div>
);
}

マウント時のみ実行する

useEffectで初回マウント時(初回レンダリング後)のみlogが画面に出力するように書き換える。
useEffectの第二引数に空配列を設定した場合は、マウント時の1回のみ実行される。
ライフサイクルメソッドでいうとcomponentDidMountと同じ動きになる。 

jsx
import "./styles.css";
import React, { useEffect, useState } from "react";
export default function App() {
//カウンターのstate
const [count,setCount] = useState(0)
//ログのstate
const [log,setLog] = useState([])
// useEffectでマウント時のみlog出力する.
// マウント時のみ実行する場合は第二引数に空配列を設定する
useEffect(() => {
setLog((prevLog)=>[...prevLog,`count${count}`])
},[]);
// カウントアップ関数
const countUp = ()=>{
setCount((prevCount)=>prevCount+1)
}
return (
<div className="App">
<div>
<h2>カウンター</h2>
<button onClick={countUp} >click me</button>
{count}
</div>
<div>
<h2>log</h2>
{log.map(
(log=><div key={log}>{log}</div>))
}
</div>
</div>
);
}

レンダリング時に毎回実行する

useEffectで毎レンダリング時にlogが出力するように書き換える。
useEffectの第二引数に設定しなかった場合は、毎レンダリング時に実行される。
ライフサイクルメソッドでいうとcomponentDidUpdateと同じ動きになる。 

jsx
import "./styles.css";
import React, { useEffect, useState } from "react";
export default function App() {
//カウンターのstate
const [count, setCount] = useState(0);
// useEffectでレンダリング時に毎回log出力する.
// 毎レンダリング時実行する場合は第二引数を設定しない
useEffect(() => {
console.log(`count${count}`);
});
// カウントアップ関数
const countUp = () => {
setCount((prevCount) => prevCount + 1);
};
return (
<div className="App">
<div>
<h2>カウンター</h2>
<button onClick={countUp}>click me</button>
{count}
</div>
</div>
);
}

※画面にlogを出した場合、その度、レンダリングが行われるため無限ループするので
コンソールに出している。

任意のstateに変更があった場合のみ実行する

useEffectでstateのcountを監視して変更があった場合のみ
ログ出力する。

jsx
import "./styles.css";
import React, { useEffect, useState } from "react";
export default function App() {
//カウンターのstate
const [count,setCount] = useState(0)
//ログーのstate
const [log,setLog] = useState([])
// useEffectでcountに変更が合った場合のみlog出力する.
// stateが変わった時のみ実行する場合は配列にそのstateを入れる
useEffect(() => {
setLog((prevLog)=>[...prevLog,`count${count}`])
},[count]);
// カウントアップ関数
const countUp = ()=>{
setCount((prevCount)=>prevCount+1)
}
return (
<div className="App">
<div>
<h2>カウンター</h2>
<button onClick={countUp} >click me</button>
{count}
</div>
<div>
<h2>log</h2>
{log.map(
(log=><div key={log}>{log}</div>))
}
</div>
</div>
);
}

useEffect部分の処理の順としては下記となる。

  1. 初期表示時、countに初期値0が設定する
  2. countに初期値が設定されたため、画面レンダリング後にuseEffect内のsetLogが実行される
  3. click meボタン押下して、countに+1する
  4. countが更新されたため、画面が再レンダリングされる
  5. useEffect内のsetLogが実行される

任意のstateに変更があった場合のみ実行する(クリーンアップ処理あり)

上記の処理にクリーンアップ処理を追加して
stateの値が特定の値になったタイミングで初期化が行われるようにする。

jsx
import "./styles.css";
import React, { useEffect, useState } from "react";
export default function App() {
//カウンターのstate
const [count, setCount] = useState(1);
//ログーのstate
const [log, setLog] = useState([]);
// useEffectでcountに変更が合った場合のみlog出力する.
// stateが変わった時のみ実行する場合は配列にそのstateを入れる
useEffect(() => {
// ログを表示する
setLog((prevLog) => [...prevLog, `count${count}`]);
// クリーンアップ処理
// countに変更があった場合に実行される
return () => {
// ログが4行になったら初期化
setLog((prevLog) =>
prevLog.length === 4 ? [] : prevLog.map((log) => log)
);
// カウントが4になったら初期化
setCount((prevCount) => (prevCount === 4 ? 1 : prevCount));
};
}, [count]);
// カウントアップ関数
const countUp = () => {
setCount((prevCount) => prevCount + 1);
};
return (
<div className="App">
<div>
<h2>カウンター</h2>
<button onClick={countUp}>click me</button>
{count}
</div>
<div>
<h2>log</h2>
{log.map((log) => (
<div key={log}>{log}</div>
))}
</div>
</div>
);
}

useEffect部分の処理の順としては下記となる。

  1. 初期表示時、countに初期値0が設定する
  2. countに初期値が設定されたため、画面レンダリング後にuseEffect内のsetLogが実行される
  3. click meボタン押下して、countに+1する
  4. countが更新されたため、画面が再レンダリングされる
  5. useEffect内のクリーンアップ処理が実行される
  6. useEffect内のsetLogが実行される

アンマウント時のみ実行する(クリーンアップ処理のみを実行する)

Counterコンポーネントがアンマウントされた時のみ、
画面のログに「LOG END」が出力される。
ライフサイクルメソッドのcomponentWillUnmountと同じ動きになる
Counterコンポーネント内にstateでLogを持たせた場合は、アンマウントした時に
Counterコンポーネントが破棄されてLogも消えてしまい、「LOG END」が出力できないため
stateのlogは親コンポーネントに持たせ、propsでCounterコンポーネントに渡している。

jsx
import "./styles.css";
import React, { useEffect, useState } from "react";
// Counterコンポーネント
const Counter = ({ log, setLog }) => {
//カウンターのstate
const [count, setCount] = useState(0);
useEffect(() => {
// Counterコンポーネントがアンマウント時のみに実行
// returnで関数を返した場合はアンマウント時に実行される
return () => setLog((prev) => [...prev, `LOG END`]);
}, []);
// カウントアップ関数
const countUp = () => {
setCount((prevCount) => prevCount + 1);
};
return (
<div className="App">
<div>
<h2>カウンター</h2>
<button onClick={countUp}>click me</button>
{count}
</div>
{log.map((log, index) => (
<div style={{ textAlign: "center" }} key={index}>
{log}
</div>
))}
</div>
);
};
export default function App() {
const [state, setState] = useState(true);
//ログのstate
const [log, setLog] = useState([]);
// Counterコンポーネントに渡すpropsをまとめる
const prop = { log, setLog };
return (
<div>
{state && (
<div>
{/* stateを切り替えCounterを破棄してアンマウントを実行させる */}
<button onClick={() => setState(!state)}>アンマウント実行</button>
{/* stateのlogを渡す */}
<Counter {...prop} />
</div>
)}
{!state && (
<div>
<button onClick={() => setState(!state)}>戻る</button>
</div>
)}
</div>
);
}

useEffect部分の処理の順としては下記となる。

  1. アンマウント実行ボタンを押下して、Cunterコンポーネントを破棄する。
  2. 再レンダリングされる
  3. useEffectのクリーンアップ処理が実行される

useEffect部分の説明

useEffect部分を少し詳しまとめる

jsx
useEffect(() => {
// Counterコンポーネントがアンマウント時のみに実行
// returnで関数を返した場合はアンマウント時に実行される
return () => setLog((prev) => [...prev, `LOG END`]);
}, []);
  • useEffect第一引数にはreturnでのクリーンアップ処理のみを返すようにする。
    • 他の処理(retrunでない)を書くとマウント時に実行されてしまうため
  • useEffect第二引数には[]を設定する。
    • 本来は[]を設定した場合はマウント時のみ実行されるが、クリーンアップ処理のみ書くことで
      アンマウント時のみ実行するようにしている

マウント&アンマウント時のみ実行する

下記以外のソースは「アンマウント時のみ実行する」と同じ。
useEffect第一引数で任意の処理とクリーンアップ処理を書く

jsx
useEffect(() => {
// マウント時のみ実行される
setLog((prev) => [...prev, `start`]);
// Counterコンポーネントがアンマウント時のみに実行
// returnで関数を返した場合はアンマウント時に実行される
return () => setLog((prev) => [...prev, `LOG END`]);
}, []);
  • useEffect第一引数にはマウント時に実行する処理とreturnでクリーンアップ関数を返すようにする。
    • returnで返す関数はアンマウント時のみ実行される
  • useEffect第二引数には[]を設定する。
    • 第二引数で[]を設定するとマウント時実行となるがクリーンアップ処理を書くこと
      でアンマウント時も実行される

useEffect部分の処理の順としては下記となる。

  1. 初期表示時、レンダリング後にuseEffect内のsetLogが実行される
  2. アンマウント実行でCounterコンポーネントが破棄する
  3. 再レンダリングされる
  4. useEffect内のクリーンアップ処理が実行される

まとめ

  • useEffectは関数コンポーネントでライフサイクルメソッドを持たせるための機能
  • クラスコンポーネントで利用していたライフサイクルメソッドを一つの関数で表現できる
  • uesEffectの第一引数で指定できる関数は二つ
    • 任意の処理
    • クリーンアップ処理
  • uesEffectの第二引数で指定できる実行タイミングは三つあり すべて下記タイミングのレンダリング後に実行される。
    • 毎レンダリング時
    • 指定したstateの変更時
    • マウント時の1回のみ

参考

【React Hooks】useEffectの基本的な動きを理解して使いこなそう
React HooksのuseEffectで関数コンポーネントにライフサイクルを持たせる
useEffectフックのしくみ
【useEffect】React hookが便利すぎる

新着記事

目次
タグ一覧
top