ReactのuseEffectでライフサイクルを再現する方法を徹底解説!初心者でもわかるReactのライフサイクル入門
生徒
「先生、Reactのライフサイクルって何ですか?クラスコンポーネントの話を聞いたことがありますけど、今は関数コンポーネントが主流なんですよね?」
先生
「そうですね。Reactの関数コンポーネントでは、useEffectというフックを使うことで、クラスコンポーネントのライフサイクルと同じような動きを再現できます。」
生徒
「なるほど!でもuseEffectってどうやって使うんですか?」
先生
「それでは、useEffectを使ってライフサイクルを再現する方法を順番に見ていきましょう!」
1. Reactのライフサイクルとは?
Reactの「ライフサイクル」とは、コンポーネントが画面に現れてから消えるまでの流れを指します。人間に例えると、「生まれる(マウント)」「成長する(更新)」「消える(アンマウント)」のようなものです。
クラスコンポーネントでは、componentDidMountやcomponentDidUpdateなどのメソッドを使って、この流れを制御していました。しかし、関数コンポーネントではそれらの代わりにuseEffectフックを使います。
2. useEffectの基本構文を理解しよう
useEffectは、Reactの関数コンポーネントで副作用(サイドエフェクト)を扱うためのフックです。副作用とは、データの取得、タイマー処理、DOM操作など、コンポーネントの外に影響を与える動作のことを指します。
import React, { useEffect } from "react";
function Example() {
useEffect(() => {
console.log("コンポーネントがマウントされました!");
});
return <h1>Hello React!</h1>;
}
export default Example;
useEffectは、コンポーネントが描画されたあとに呼び出されます。つまり、初回表示時にも実行されるため、これが「マウント時の処理」となります。
3. マウント時だけ実行するには?
通常のuseEffectは、状態(ステート)が変わるたびに実行されます。しかし、初回だけ(マウント時だけ)実行したい場合は、第二引数に空の配列[]を指定します。
import React, { useEffect } from "react";
function MountExample() {
useEffect(() => {
console.log("最初の一回だけ実行されます!");
}, []);
return <h2>マウント時だけ実行!</h2>;
}
export default MountExample;
この書き方は、クラスコンポーネントでいうcomponentDidMountと同じ役割を持っています。
4. 更新時に処理を実行するには?
次に、特定の状態(state)が変化したときだけ処理を実行したい場合です。第二引数にその変数を指定することで、Reactが監視してくれます。
import React, { useState, useEffect } from "react";
function UpdateExample() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`カウントが${count}に更新されました`);
}, [count]);
return (
<div>
<h2>カウント: {count}</h2>
<button onClick={() => setCount(count + 1)}>カウントアップ</button>
</div>
);
}
export default UpdateExample;
このようにすることで、countが更新されたタイミングだけ処理が動くようになります。これは、クラスコンポーネントのcomponentDidUpdateと同じ働きです。
5. アンマウント時の処理を再現するには?
最後に、コンポーネントが削除(アンマウント)されるときの処理を見てみましょう。これは、useEffectの中で「クリーンアップ関数」を返すことで実現できます。
import React, { useEffect } from "react";
function UnmountExample() {
useEffect(() => {
console.log("コンポーネントがマウントされました!");
return () => {
console.log("コンポーネントがアンマウントされました!");
};
}, []);
return <h3>アンマウントの確認コンポーネント</h3>;
}
export default UnmountExample;
このreturnの中の処理は、コンポーネントが削除される直前に呼び出されます。例えば、タイマーを止めたり、イベントリスナーを解除したりするときに使います。
6. 3つのライフサイクルをまとめて理解しよう
ここまでの内容を整理すると、useEffectは1つの構文でマウント・更新・アンマウントの3つを再現できる強力なフックだとわかります。
- マウント時:コンポーネントが最初に表示されるとき
- 更新時:指定した変数が変化したとき
- アンマウント時:コンポーネントが削除されるとき
この3つをうまく使うことで、APIからのデータ取得やタイマー制御など、実際のWebアプリ開発に欠かせない動作を安全かつ簡単に実装することができます。
まとめ
ここまでReactの関数コンポーネントにおけるライフサイクルの管理方法、特にuseEffectフックの活用術について詳しく解説してきました。React開発において、コンポーネントがどのタイミングで描画され、どのタイミングで破棄されるかを制御することは、アプリケーションのパフォーマンス向上やバグ回避のために非常に重要です。
従来のクラスコンポーネントでは、componentDidMountやcomponentDidUpdateといった複数のメソッドを使い分ける必要がありましたが、モダンなReact開発ではuseEffect一つでこれらをスマートに完結させることができます。本記事で学んだ「第二引数の依存配列の使い方」や「クリーンアップ関数の役割」は、実務レベルのコードを書く際にも必ず必要になる知識です。
useEffectの活用パターンをおさらい
useEffectを使いこなすためのポイントは、第二引数に渡す配列の状態によって、実行されるタイミングが劇的に変わるという点です。以下の表に主要なパターンを整理しました。
| 指定方法 | 実行タイミング | 主な用途 |
|---|---|---|
useEffect(() => { ... }) (なし) |
レンダリングの度すべて | デバッグ用のログ出力など |
useEffect(() => { ... }, []) (空配列) |
初回マウント時のみ | APIからのデータ取得、初期設定 |
useEffect(() => { ... }, [data]) (特定の値) |
dataが変更されたとき | 特定の値に連動した再取得や計算 |
実践的なサンプルプログラム:タイマー機能の実装
学んだことを活かして、マウント時にカウントを開始し、アンマウント時にしっかりとメモリを解放(タイマーを停止)する実践的なサンプルコードを作成してみましょう。ここでは、setIntervalを使ったカウントアップ処理を例に挙げます。
import React, { useState, useEffect } from "react";
function TimerComponent() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
// マウント時にタイマーを開始
const intervalId = setInterval(() => {
setSeconds((prev) => prev + 1);
console.log("タイマーが動いています...");
}, 1000);
// アンマウント時にクリーンアップを実行
// これを忘れるとメモリリークの原因になります
return () => {
clearInterval(intervalId);
console.log("タイマーを停止しました");
};
}, []); // 空の配列なのでマウント時とアンマウント時のみ動作
return (
<div className="p-4 border rounded bg-light">
<h3>経過時間: {seconds}秒</h3>
<p>このコンポーネントを非表示にするとタイマーは止まります。</p>
</div>
);
}
export default TimerComponent;
このように、useEffectの中で定義した処理を、returnで解除する仕組みは、イベントリスナーの登録解除やWebSocketの切断など、リソースを適切に管理する上で欠かせないテクニックです。
SEOを意識したReact開発のポイント
Reactを使用したシングルページアプリケーション(SPA)において、useEffectによるデータ取得はSEOにも影響を与えることがあります。検索エンジンにコンテンツを正しく認識させるためには、サーバーサイドレンダリング(SSR)が可能なNext.jsの利用も検討すべきですが、クライアントサイドでの適切なライフサイクル管理が基本となります。
例えば、useEffect内で非同期処理を行う場合、ローディング状態を適切に管理することで、ユーザー体験(UX)を高めることができます。Googleの評価基準であるコアウェブバイタル(Core Web Vitals)を意識し、不必要な再レンダリングを抑える設計を心がけましょう。
生徒
「先生、今回の内容でuseEffectの使い方がかなり明確になりました!特に、アンマウント時のクリーンアップ処理(return関数)がなぜ必要なのかがよく分かりました。放っておくとブラウザが重くなっちゃうんですね。」
先生
「その通りです!特にタイマーや外部APIとのリアルタイム通信を行うときは、後片付けをしないとメモリリークの原因になります。JavaScriptの基礎的な知識も重要ですが、React特有のルールを覚えることで、より堅牢なWebアプリケーションが作れるようになりますよ。」
生徒
「第二引数の配列についても、空にするか特定の変数をいれるかで挙動が全然違いますね。特定のステートを監視して自動で処理を走らせる仕組みは、効率的で感動しました。」
先生
「そうですね。ただ、依存配列(Dependency Array)の指定を間違えると、無限ループに陥ることもあるので注意が必要です。開発中はブラウザのコンソールをこまめにチェックして、意図しない回数の実行が行われていないか確認する習慣をつけましょう。」
生徒
「無限ループは怖いですね…。まずは単純なマウント時のデータ取得から練習して、徐々に複雑な連動処理に挑戦してみようと思います!」
先生
「素晴らしい意気込みですね!Reactには他にも便利なフックがたくさんありますが、まずはこのuseStateとuseEffectをマスターすることが脱初心者の第一歩です。頑張ってくださいね!」