【React】なぜuseEffectが2回動くの?Strict Modeの「二重実行」に隠された、開発者を守るための優しさ
Reactを学び始めて、最初に誰もが遭遇する「謎の現象」があります。
「よし!画面が開いたときに一回だけ実行したいから、`useEffect` を書こう」
そう思ってブラウザのコンソールを開いてみると……
画面が開いたよ!
画面が開いたよ!
「えっ!? なんで2回出力されてるの!? コードの書き方、間違えた……!?」
大丈夫ですよ、安心してくださいね。あなたの書いたコードは1ミリも間違っていません。バグでもありませんし、パソコンの調子がおかしいわけでもありません。
実はこれ、Reactがあなたのために意図的にやってくれている「安全のための予行練習」なのです。
今回は、Reactの「Strict Mode(厳格モード)」がなぜ `useEffect` を2回実行するのか、そしてそれがどうやって私たちの開発を助けてくれているのかを、身近な例えを交えて優しく紐解いていきましょう!
—
1. そもそも「Strict Mode(厳格モード)」ってなぁに?
Reactでアプリを作るとき、コードのどこかで `
これは例えるなら、「超優秀な自動車の車検・安全点検モード」です。
- 本番環境(ユーザーが見る実際の画面): 1回だけ実行される(普通に動く)
- 開発環境(あなたがコードを書いている手元の画面): あえて2回実行して、危険がないかチェックする
そう、この2回実行される現象は「あなたがコードを書いている開発中だけ」に起きる特別なチェック機能なのです。本番で公開したときには勝手に2回動くことはありませんので、まずはホッと胸をなでおろしてくださいね。
—
2. なぜわざわざ「2回」実行するの?(イベント会場の例え話)
では、なぜReactはわざわざ2回も動かすのでしょうか?
ちょっと身近な「イベント会場の設営と撤去」で考えてみましょう。
あなたがイベントの設営担当者だとします。
「ステージの上に、大きな案内看板を1つ設置してください」と頼まれました。
- 1回目の設置(マウント): 看板を置いた。
- 撤去(クリーンアップ): 一度、看板を片付けた。
- 2回目の設置(再マウント): もう一度、看板を置いた。
もしあなたが「片付け」を完璧にやっていれば、2回目に設置し直しても「看板は1枚だけ」正しく立っていますよね。
でも、もしあなたが片付けを忘れて、1回目の看板を置いたまま2回目の設置をしてしまったらどうでしょう?
ステージの上には「2枚の看板が重なってぐちゃぐちゃ」になってしまいます。
Reactはこれと同じテストを、ブラウザの中で一瞬のうちに行っているのです。
1. 副作用を実行する(看板を置く)
2. すぐに破棄してみる(看板を片付ける)
3. もう一度実行してみる(看板をもう一度置く)
「片付け(クリーンアップ関数)」を書き忘れていると、2回実行された瞬間に画面の表示がおかしくなったり、処理が重くなったりします。Reactはあえて2回動かすことで、「ねえ、片付けのコード忘れてない? 本番で事故が起きる前に気づいてね!」と、優しく教えてくれているのです。
—
3. 難しい言葉「冪等性(べきとうせい)」を世界一わかりやすく
Reactの公式ドキュメントなどを読んでいると、「冪等性(べきとうせい)」という難しそうな漢字が出てくることがあります。
難しく聞こえますが、意味はとってもシンプルです。
> 冪等性とは:
> 「1回やっても、100回やっても、得られる結果が同じであること」
身近なもので例えてみましょう。
- 部屋の照明スイッチ:
「ON」のボタンは、1回押しても部屋が明るくなりますし、間違えて「ON」を10回連打しても部屋は「明るい状態」のままですよね。これが冪等性がある(安心な)状態です。
- お会計のレジ:
「1,000円払う」ボタンを1回押したら1,000円引き落とされますが、もしシステムのエラーで2回押された判定になり、2,000円引き落とされたら大問題ですよね! これは冪等性がない(危険な)状態です。
Reactのコンポーネント(画面の部品)も、照明のスイッチのように「何回組み立て直されても、常に同じ正しい状態になってほしい」のです。
Strict Modeで `useEffect` を2回実行することは、「あなたの書いた処理は、2回連続で動いても安全な『冪等性』を持っていますか?」 というテストをしてくれているわけですね。
—
4. よくある失敗と「クリーンアップ関数」の魔法
では、実際にどんなコードだと危険で、どう直せばいいのかを見てみましょう!
よくあるのが「タイマー(setInterval)」や「イベントの監視(addEventListener)」を使うケースです。
✕ 良くない例:片付け(クリーンアップ)を忘れている
import { useEffect, useState } from ‘react’;
function BadCounter() {
const [count, setCount] = useState(0);
useEffect(() => {
// 1秒ごとにカウントを+1するタイマーを作る
setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
// びくびく… クリーンアップ(片付け)を書いていない!
}, []);
return
カウント: {count}
;
}
これをStrict Modeで動かすと、最初にタイマーが2つ作られてしまいます!
すると、1秒ごとにカウントが `1, 2` ではなく、`2, 4, 6, 8…` と倍速で増えていく恐ろしい現象が起きます。「あれ!? カウントが爆速で進むんだけど!?」というReact初心者あるあるのバグですね。
◯ 正しい例:しっかり片付け(クリーンアップ)を書いている
これを防ぐために、`useEffect` の中で「使い終わったら片付ける関数」を `return` してあげます。
import { useEffect, useState } from ‘react’;
function GoodCounter() {
const [count, setCount] = useState(0);
useEffect(() => {
// タイマーを開始し、そのIDをメモしておく
const timerId = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
// ★ ここが超重要!「片付けの関数」を返す
// Reactがコンポーネントを破棄するとき(または2回実行の合間)にこれが呼ばれます
return () => {
clearInterval(timerId); // タイマーをきれいに解除!
};
}, []);
return
カウント: {count}
;
}
こう書かれていれば、Reactが開発環境で2回実行(作成→破棄→作成)しても、1回目のタイマーは綺麗に消去され、2回目のタイマーだけが正しく動き続けます。
カウントも正常に `1, 2, 3…` と増えていきますよ!
—
5. エディタに貼り付けて試せる!実践サンプルコード
ぜひお手元の環境(ViteやNext.jsなど)で、以下のコードをコピー&ペーストして動かしてみてください。
ブラウザのコンソール(F12キーで開く画面)を見ながら触ると、Reactの動きが手に取るようにわかりますよ!
import React, { useState, useEffect } from ‘react’;
export default function StrictModeDemo() {
const [logs, setLogs] = useState([]);
// ログを画面に追加するための補助関数
const addLog = (message) => {
setLogs((prev) => […prev, `${new Date().toLocaleTimeString()} – ${message}`]);
};
useEffect(() => {
// 1. 副作用の実行(設営)
console.log(‘🟢 [実行] useEffectが動き出しました!’);
addLog(‘🟢 [実行] useEffectが動き出しました!’);
// 2. クリーンアップ関数(撤去)
return () => {
console.log(‘🔴 [破棄] クリーンアップ関数が呼ばれました!’);
// ※setStateはアンマウント時に動かすと警告になることがあるため、コンソールログで確認します
};
}, []); // 空の依存配列=画面が開いたとき(マウント時)のみ
return (
Strict Mode 動作確認コンポーネント
ブラウザのデベロッパーツール(コンソール)を開いて確認してみましょう!
画面上のログ記録:
{logs.map((log, index) => (
{log}
))}
コンソール画面を見ると、
🟢 [実行] ➔ 🔴 [破棄] ➔ 🟢 [実行]の順番で、Reactがわざと「セットアップと片付け」をテストした形跡が見えるはずです!
);
}
—
まとめ:2回実行はReactからの「愛のメッセージ」
最初に `useEffect` が2回動くのを見たときは、「何か悪いことをしちゃったかな……」と不安になりますよね。
でも、仕組みを知ってみると、どうでしょう?
Reactは、あなたの作ったアプリが本番環境でユーザーの手によって動かされたとき、絶対に事故やバグが起きないように、陰でそっと予行練習をして守ってくれていたのです。
1. 2回実行されるのは開発環境(Strict Mode)だけ! 本番では1回だから安心してくださいね。
2. 2回動いて困る処理があったら、それは「片付け(クリーンアップ)」を忘れているサイン!
3. `return () => { … }` でしっかりお掃除をする習慣をつけましょう!
この仕組みが理解できたら、あなたはもうReactの初学者を脱出して、一歩先のアドバンスな開発者へ足を踏み入れていますよ。
バグを怖がらず、ぜひこの「2回実行」を味方につけて、快適なReact開発を楽しんでくださいね!応援しています!

コメント