こんにちは!Reactの学習、楽しく進められていますか?
画面がスイスイ動くのは楽しいけれど、新しい概念や「おや?」と思う挙動に出会うと、ふと手が止まってしまいますよね。
特に、Reactを学び始めて多くの人が「あれっ!?」とパニックになりがちで、かつ現場のプロもめちゃくちゃ大切にしているテーマがあります。それが、「開発環境(Strict Mode)で、`useEffect`がなぜか2回実行されてしまう現象」です。
コンポーネントを読み込んだだけで、コンソールにログが2回ポツポツと表示されるのを見ると、「私のコード、何か変なバグを埋め込んでしまったのでは…!?」と冷や汗が出ますよね。
でも、どうか安心してください。あなたのコードは間違っていません。それはReactからの「優しさ」であり、未来のバグを防ぐための重要なサインなんです。今日は、身近な例えを交えながら、この「2回実行の秘密」と「クリーンアップ関数」という心強い味方について、ゆっくり紐解いていきましょう!
—
1. なぜ? Strict Modeでの「2回実行」の正体
まずは、なぜこんなことが起きるのか、その理由からお話ししますね。
Reactには、Strict Mode(厳格モード)という、開発中だけにこっそり作動する「お節介なチェック機能」があります。これは、あなたのコードの健康診断をしてくれる専属トレーナーのような存在です。
このトレーナーは、開発環境である実験を行う習性があります。それは、「コンポーネントを画面に出したり(マウント)、一瞬で引っ込めたり(アンマウント)、もう一回出したり(再マウント)」というリハーサルを、ページを開いた瞬間に猛スピードで行うのです。
身近な例えで考えてみましょう:自動ドアのセンサー
想像してみてください。あなたは今、新しいお店の「自動ドアのセンサー」を作っています。
お店の入り口に人が来たら(コンポーネントが表示されたら)、ピッと音が鳴るプログラムを `useEffect` で書きました。
普通のモードだと、お客さんが1人来たら「ピッ」と1回鳴って終わりです。
しかし、Strict Modeという厳しいチェック官が来ると、こう言います。
> 「ちょっと待って! お客さんが入ってきた直後に、一瞬だけ外に出て、もう一回入り直したときの動作テストもしておこう!」
つまり、
1. お店に入る(1回目の実行)
2. 一瞬外に出る(クリーンアップの実行)
3. もう一度お店に入る(2回目の実行)
というリハーサルを、私たちの知らないところで一瞬のうちにやってのけるのです。これが、開発環境で `useEffect` が2回動く仕組みです。本番環境(ユーザーが実際に使う公開後)では、このリハーサルは行われず、通常通り1回しか実行されないので安心してくださいね。
—
2. なぜこんなテストをするの?(Reactの深い愛)
「わざわざ2回も動かして、混乱させないでよ!」って思いますよね。でも、これにはちゃんとした深い理由があるんです。
Webアプリを作っていると、こんな問題がよく起きます。
- ページから別のページに移動したのに、裏でずっとタイマーが動き続けてメモリを食いつぶしている。
- サーバーとの通信が中途半端に残っていて、エラーが起きる。
これらはすべて、「画面から消えたあとの後片付け(お掃除)」を忘れていることが原因です。
Strict Modeは、「ほら、画面から一瞬引っ込んだときに、ちゃんとお部屋を片付け(クリーンアップ)できてますか? 片付けを忘れていると、本番環境でメモリ漏れ(メモリリーク)という大変なバグになりますよ!」ということを、事前に教えてくれているんです。
つまり、2回実行されるのは、「あなたのコードが、お片付け上手かどうかを試されているテスト」なんだと思ってあげてください。
—
3. 「お片付け」の主役:クリーンアップ関数とは?
ここで登場するのが、`useEffect` の中でひっそりと返される関数、クリーンアップ関数です。
難しく考える必要はありません。これは、「この部屋を出るときに、電気を消して、鍵を閉めてね」というお掃除の予約です。
言葉だけだとイメージしにくいと思うので、具体的なコードを見てみましょう。ここでは、毎秒カウントが増える「タイマー」を例にします。
実用的なサンプルコード
import React, { useState, useEffect } from ‘react’;
function TimerComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
// — 【セットアップ】コンポーネントが表示された時に動く処理 —
console.log(‘【セットアップ】タイマーをスタートします!’);
// 1秒ごとにカウントを1ずつ増やすタイマーをセット
const timerId = setInterval(() => {
setCount((prevCount) => prevCount + 1);
}, 1000);
// — 【クリーンアップ関数】コンポーネントが消える時(お片付け)に動く処理 —
return () => {
console.log(‘【クリーンアップ】タイマーを止めます(お片付け)!’);
// 動きっぱなしを防ぐために、セットしたタイマーをしっかりクリアする
clearInterval(timerId);
};
}, []); // 依存配列が空なので、最初のマウント時に実行される
return (
タイマーの実験場
経過時間: {count} 秒
※開発者ツールのコンソールを開いて、ログの動きを確認してみてくださいね。
);
}
export default TimerComponent;
このコードをStrict Modeが有効な環境で動かすと、コンソールには次のような順番でログが表示されます。
1. `【セットアップ】タイマーをスタートします!` (1回目)
2. `【クリーンアップ】タイマーを止めます(お片付け)!` (Strict Modeによる一瞬のお片付け)
3. `【セットアップ】タイマーをスタートします!` (本番用の2回目・再スタート)
もし、ここに「クリーンアップ関数(お片付けの仕組み)」書き忘れていると、タイマーが止まらないまま2つ目が重なって動き出してしまい、時間の進み方が倍速になったり、予期せぬバグを引き起こしたりします。
Strict Modeはこのミスを未然に防いでくれる、最高に頼れる相棒なのです。
—
4. 初学者のあなたが今日から意識すべきポイント
「ふう、なんだか仕組みはわかったけれど、開発中にコンソールが2回ログを出すのがやっぱりちょっと気になる……」という方へ。
実務や学習において、以下の心構えを持っておくと心がとても軽くなりますよ。
1. 「開発中の2回実行は仕様だ」と割り切る
コンソールにログが2回出るのは、Reactが「ちゃんと動いてるよー、お片付けもできるかなー?」とチェックしている証拠です。エラーでアプリが壊れているわけではないので、まずは深呼吸しましょう。
2. 「外の世界」と繋がるときは、必ずクリーンアップを書く癖をつける
- `setInterval` や `setTimeout` などのタイマーを使うとき
- `window.addEventListener` などのイベントリスナーを登録するとき
- 外部のサーバーとリアルタイム通信(WebSocketなど)をつなぐとき
これらはすべて「自分のお部屋の外」とやり取りする仕組みです。お部屋を出るときは、「つないだコードを元に戻す(片付ける)」処理を `return` の中に必ず書く、とセットで覚えておいてください。
3. どうしても気になる場合の裏技(※最終手段)
どうしてもStrict Modeの挙動に精神が削られる場合は、プロジェクトの根幹にある `index.js` (または `main.js` / `App.js`)の `
ただ、これはあくまで「一時しのぎ」です。アプリが大きくなったときにバグを見つけにくくなるため、基本的にはStrict Modeの環境に慣れていくのが、将来のあなたを助ける一番の近道ですよ。
—
まとめ:Reactからのメッセージを受け取ろう
いかがでしたでしょうか?
「Strict Modeでの二重実行」は、最初に出会うとちょっとビビってしまうReactの仕様の一つです。
でも、その裏側には「本番環境でユーザーが使うときに、絶対にメモリリークやバグを起こさせない!」というReactチームの強いこだわりと優しさが隠されています。
コンソールが2回動いているのを見かけたら、
- 「おっ、Reactクンがちゃんとお片付けのテストをしてくれてるな」
- 「よし、このエフェクトには綺麗なお片付け(クリーンアップ関数)を用意しておこう!」
そう心の中で微笑みながら、コードを書いてみてくださいね。
あなたのReact学習の旅が、少しでも楽しく、心温まるものになりますように。応援しています!

コメント