【実務・中級編】 React Strict Modeにおける二重実行の理解 – React実践ガイド

React Strict Modeの「二重実行」を恐れるな。それは君のコードを強くする「試練」だ

フロントエンドの現場で、こんな場面に遭遇したことはないだろうか。

`useEffect` 内でログを仕込んだら、開発環境のコンソールに同じログが2回流れてくる。「あれ?バグか?」「リクエストが2回飛んでるじゃないか!」と焦って、慌てて依存配列を調整したり、無理やり `useRef` でフラグを立てて実行を抑制したりする……。

はっきり言おう。それ、Reactからの「もっと綺麗に書け」という愛のムチだ。

今日は、なぜReactが開発環境でわざわざ副作用を二回実行するのか、そしてその裏にある「冪等性(べきとうせい)」というプロフェッショナルの必須概念について、現場の視点から解説する。

—

なぜ「二回」なのか?:リアクティブな世界への招待

React Strict Mode(``)がコンポーネントを二回マウントし、`useEffect` を二回実行するのには明確な理由がある。それは、「この副作用は、何度繰り返されても壊れない状態になっているか?」というテストを強制しているからだ。

Reactの世界において、コンポーネントは「UIを生成する関数」であり、副作用(API通信、購読、DOM操作)はそれに付随する「おまけ」だ。本来、副作用は「開始」と「終了(クリーンアップ)」が正しく対になっていれば、何度繰り返されても安全であるべきなんだ。

もし、2回実行されただけで挙動がおかしくなるようなコードであれば、それは「Reactのライフサイクルを正しく制御できていない」というシグナルだ。例えば、ユーザーが画面を行き来しただけで購読が重複したり、メモリリークを起こしたりするリスクを、開発環境のうちに炙り出しているわけだ。

—

現場で使える「冪等性」を守る実装パターン

では、どう書けば「2回実行されても安全なコード」になるのか。実務で最もよく使う「購読(Subscription)」のパターンで見てみよう。

import { useEffect, useState } from ‘react’;

const ChatRoom = ({ roomId }) => {
useEffect(() => {
// 1. 外部リソースへの接続を開始(セットアップ)
const connection = createConnection(roomId);
connection.connect();
console.log(`ルーム ${roomId} に接続しました`);

// 2. クリーンアップ関数を定義(ここが最重要!)
// Strict Modeで2回実行される際、1回目の接続をここで確実に切断する
return () => {
connection.disconnect();
console.log(`ルーム ${roomId} から切断しました`);
};

// 依存配列に roomId があるため、roomIdが変わるたびに
// 「切断→再接続」のフローが正しく実行される
}, [roomId]);

return

チャットルーム: {roomId}

;
};

このコードが美しい理由

  • クリーンアップの完備: `return` で返された関数が、次に `useEffect` が実行される直前や、コンポーネントがアンマウントされるタイミングで確実に呼ばれる。
  • 冪等性の担保: どんなに接続と切断を繰り返しても、常に「現在の状態」と「外部の同期先」が1対1で一致する。これがReactが求める「宣言的」な副作用の姿だ。

—

よくある「悪い癖」と脱却のヒント

たまに、`useEffect` の中でフラグ変数を作って「2回目以降は実行しないようにする」といったハックを見かけるが、それは絶対におすすめしない。

// ❌ やってはいけないこと
useEffect(() => {
if (ref.current) return; // 実行をガードする
ref.current = true;
doSomething();
}, []);

これは「Reactのライフサイクルから逃げている」状態だ。これをしてしまうと、将来的に `roomId` が変更されたときや、コンポーネントが再利用される際に予期せぬバグを引き起こす。Reactが提供する「クリーンアップ関数」という仕組みを信じて、正攻法で書くのが一番の近道だ。

—

まとめ:Strict Modeはデバッグの強力な味方

Strict Modeでの二重実行は、あなたのコードの「クオリティチェック」だ。

1. クリーンアップ関数が書かれているか?
2. 副作用の開始と終了が対称的か?
3. 依存配列に漏れはないか?

これらを意識するだけで、あなたの書くコンポーネントは格段に堅牢になる。もし2回実行されて困る処理があるなら、それは「Reactの外側の世界(外部ストレージやグローバルなDOM)」と綺麗に同期できていないというサインだ。

恐れず、むしろ「自分のコードをテストしてくれてありがとう」という心持ちで、Strict Modeと向き合ってみてほしい。そうすれば、君もフロントエンドの真のアーキテクトに一歩近づけるはずだ。

現場からは以上だ。また何かあればいつでも聞いてくれ。

コメント

タイトルとURLをコピーしました