ReactのuseEffect依存配列を徹底解説!初心者でも理解できるDependency Arrayの仕組み
生徒
「先生、ReactのuseEffectを勉強しているんですが、依存配列っていうのがよく分からないんです。」
先生
「いい質問ですね。依存配列(Dependency Array)は、useEffectを使う上でとても重要なポイントなんですよ。」
生徒
「依存配列がどういう役割を持っているんですか?」
先生
「それでは、依存配列がどのように使われているのか、実際のコードを交えながら解説していきましょう。」
1. useEffectとは?
まず前提として、useEffect(ユーズエフェクト)は、Reactのフック(Hooks)のひとつで、副作用処理(サイドエフェクト)を行うために使います。副作用とは「画面に表示する処理とは直接関係がないけれど必要な処理」のことで、例えば次のようなものがあります。
- APIからデータを取得する(サーバー通信)
- ブラウザのイベントを登録する(スクロールやリサイズなど)
- タイマーを設定する(数秒後に処理を動かす)
ポイントは、これらが「自動で起きる処理」になりやすいことです。何も考えずに書くと、画面の更新のたびに何度も実行されてしまい、通信が増えたり動きが重くなったりします。そこで登場するのが「依存配列(Dependency Array)」で、useEffectを“いつ実行するか”をコントロールするための仕組みです。
まずは「画面が表示された直後に一度だけメッセージを出す」超シンプルな例を見てみましょう。
import React, { useEffect, useState } from "react";
function App() {
const [text, setText] = useState("こんにちは!");
useEffect(() => {
setText("ようこそ!");
}, []);
return (
<div>
<h1>{text}</h1>
</div>
);
}
export default App;
このようにuseEffectは「画面が表示されたタイミングで、表示以外の処理を追加したい」ときに便利です。そして、その実行タイミングを安全に決めるために、次で説明する依存配列が重要になります。
2. 依存配列(Dependency Array)の役割
依存配列は、useEffectの第2引数に指定する配列のことです。Reactはこの配列の中身を見て、「どの値が変わったときに、useEffectを再実行するか」を判断します。
イメージとしては「観察対象を登録しておくリスト」のようなもので、配列の中に書いた変数が変化したときだけ処理が動きます。たとえば、画面に出す文字(state)が変わったときだけ何かしたい、という場面で役立ちます。
ここが大事で、依存配列に書かれていない値の変化には、基本的にuseEffectは反応しません。逆に言うと「この処理は何をきっかけに動くべきか」を自分で宣言するのが依存配列です。
基本の書き方は次の通りです。
useEffect(() => {
// 実行したい処理
}, [依存する値]);
超シンプルに「ボタンでメッセージが変わったら、その変化をきっかけに処理を動かす」例を見てみましょう。
import React, { useEffect, useState } from "react";
function App() {
const [message, setMessage] = useState("こんにちは!");
useEffect(() => {
console.log("messageが変わりました:", message);
}, [message]);
return (
<div>
<h1>{message}</h1>
<button onClick={() => setMessage("ボタンがクリックされました!")}>
変更
</button>
</div>
);
}
export default App;
この例では、依存配列にmessageを書いているので、ボタンでmessageが変わったタイミングだけuseEffectが動きます。「毎回じゃなくて、必要なときだけ動かす」ためのスイッチが依存配列、と考えると理解しやすいです。
3. 依存配列の具体例
次のサンプルを見てみましょう。ボタンを押すたびにカウントが増えて、その「変化」をきっかけにuseEffectが動く例です。Reactでは、画面の表示(レンダリング)と、表示とは別に動かしたい処理(副作用)を分けて考えるのがコツになります。
import React, { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("countが変わりました!", count);
}, [count]);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>カウントアップ</button>
</div>
);
}
export default App;
このコードでは、[count]と依存配列に書いているため、countの値が変わるたびにuseEffectの中身が実行されます。逆に言えば、countが変わらない限りは実行されません。つまり依存配列は「この値が変わったら、ここを動かしてね」という合図の役割を持っています。
初心者のうちは、useEffectを「何かが変わったのを合図にして動く仕組み」と捉えると理解しやすいです。今回はcountの変化が合図になっているので、ボタン操作 → count更新 → useEffect実行、という流れになります。
4. 依存配列の種類と違い
依存配列の書き方によって、useEffectの動きは変わります。大きく分けると次の3パターンです。
- 依存配列なし:コンポーネントが再描画されるたびに毎回実行される
- 空配列 []:最初の1回だけ実行される(マウント時のみ)
- 特定の値を指定:指定した値が変わったときにだけ実行される
例えば次のように書くと、マウント時に一度だけ実行されます。
useEffect(() => {
console.log("最初の1回だけ実行されます");
}, []);
5. よくある間違いと注意点
初心者がつまずきやすいのが、「依存配列に書くべき変数を忘れる」ケースです。例えばAPIを呼び出す処理の中で外部の変数を参照しているのに、その変数を依存配列に書いていないと、値が変わっても処理が実行されません。
逆に、必要のない変数まで依存配列に入れてしまうと、無駄に何度も実行されてしまいパフォーマンスが悪くなります。
依存配列は「その処理がどの変数の変化に反応するべきか」を明確にするためのリスト、と覚えておくと理解しやすいです。
6. 依存配列のイメージを身近な例で考える
依存配列をイメージするために、生活の例に置き換えてみましょう。
例えば「冷蔵庫の牛乳がなくなったら買いに行く」というルールを考えてみます。ここで「牛乳」が依存する値です。牛乳がなくなったときだけ行動(買い物)が発動します。冷蔵庫に卵や野菜が減っても、このルールでは反応しません。
Reactの依存配列も同じように、観察する対象を「配列の中に登録しておく」イメージを持つとわかりやすいです。
まとめ
ここまでReactのuseEffectにおける「依存配列(Dependency Array)」の重要性とその仕組みについて詳しく見てきました。React開発において、この依存配列をいかに正確に扱うかが、アプリケーションのパフォーマンスや予期せぬバグの回避に直結します。
最後におさらいとして、依存配列の指定方法による動作の違いをもう一度整理しておきましょう。
依存配列の挙動パターン・クイックリファレンス
| 依存配列の指定 | 実行されるタイミング | 主なユースケース |
|---|---|---|
| 指定なし(第2引数自体を省略) | レンダリングのたびに毎回実行 | デバッグ用のログ出力など(多用は厳禁) |
空の配列 [] |
最初の1回(マウント時)のみ実行 | APIからの初期データ取得、イベントリスナーの登録 |
値あり [data] |
初回 + dataの値が変化したとき |
特定の入力値に基づいた再検索、バリデーション処理 |
実践的な活用例:検索機能の連動
たとえば、検索窓に入力されたキーワード(query)が変わるたびにAPIを叩き直すような処理は、依存配列の最も得意とする場面です。
import React, { useState, useEffect } from "react";
function SearchComponent() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
useEffect(() => {
if (query === "") {
setResults([]);
return;
}
// 本来はここでAPI通信などを行う
console.log(`${query} で検索を実行中...`);
setResults([`${query}の結果1`, `${query}の結果2`]);
}, [query]); // queryが書き換わったときだけこの中身が動く
return (
<div class="p-3 border rounded">
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="キーワードを入力..."
className="form-control mb-2"
/>
<ul>
{results.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default SearchComponent;
Reactのフックは、最初は魔法のように感じるかもしれませんが、依存配列を「処理を実行するためのトリガー(引き金)」だと理解すれば、格段に扱いやすくなります。特にNext.jsなどのモダンなフレームワークを使う際にも、この基礎知識は必須となります。 「なぜか無限ループしてしまう」「値が変わったのに画面が更新されない」といった問題に直面したときは、まずこの依存配列の中身を疑ってみてください。
生徒
「先生、ありがとうございました!依存配列があるおかげで、Reactが『いつ動けばいいか』を迷わずに済んでいるんですね。」
先生
「その通りです。もし依存配列がなかったら、Reactは少し画面が変わるたびに重い処理を全部やり直してしまいますからね。効率化のための『見張り番』のようなものです。」
生徒
「さっきの空の配列 [] を指定するパターン、あれはすごく使い勝手が良そうです。ページを開いたときだけ挨拶を表示したり、データを読み込んだりするのにピッタリですね。」
先生
「よく気づきましたね。ただし、依存配列の中に含めるべき変数(useEffectの中で使っている変数)を入れ忘れると、古いデータのまま処理が動いてしまう『クロージャの罠』にハマることがあるので、そこだけは注意が必要です。ESLintなどのチェックツールを使うと、入れ忘れを防いでくれますよ。」
生徒
「なるほど。ただ闇雲に書くのではなく、どの値に注目すべきかをしっかり考えて設計してみます。まずは簡単なカウンターやフォームの連動から練習して、手に馴染ませていこうと思います!」
先生
「素晴らしい意気込みですね。実際にコードを書いて、コンソールログで動作を確認する癖をつければ、すぐにマスターできますよ。頑張ってくださいね。」