なぜReact Strict Modeは「二重レンダリング」という悪夢を見せるのか?
現場でReactを書いていると、コンソールを見て「あれ、ログが2回出ている?」と首を傾げた経験はないだろうか。特に `useEffect` の中でAPIを叩いている時、開発環境だけでリクエストが重複して飛んでいく……。
これ、実はバグではない。Reactチームが仕組んだ「あえてやる意地悪」であり、我々を未然のバグから救うための「Strict Mode」による防波堤なんだ。
今日は、なぜこの機能が重要なのか、そして実務でどう付き合っていくべきか、現場の視点から深掘りしていこう。
—
1. Strict Modeの正体:ただの「デバッグ補助ツール」ではない
結論から言うと、`React.StrictMode` はアプリケーションのレンダリング結果を変えるものではない。開発環境においてのみ、「副作用をあえて二重に実行することで、壊れやすいコードを炙り出す」ための厳しい訓練環境だ。
具体的には、以下のことを意図的に行う。
- コンポーネントの二重レンダリング: コンポーネント関数を意図的に2回呼び出す。
- 副作用の二重呼び出し: `useEffect` のセットアップ関数を、マウント→アンマウント→再マウントの順で強制的に走らせる。
「え、そんなことされたらAPIが二重に叩かれるじゃないか!」と思うかもしれない。だが、もしそこで不具合が出るなら、そのコードは本来、本番環境でも潜在的なバグを抱えているということだ。
2. なぜReactはわざわざそんなことをするのか?
現代のReactは、Concurrent Mode(並行レンダリング)が前提だ。Reactは将来的に、UIの描画を中断したり、再開したり、あるいは一旦破棄してやり直したりする可能性がある。
もし君のコードが「一度だけマウントされる」という前提で書かれていたら、Reactが裏側でレンダリングを最適化しようとした瞬間に、メモリリークや整合性の崩壊が起きる。Strict Modeは、「お前のコードは、いつ何度レンダリングされても大丈夫な設計になっているか?」という問いを突きつけているわけだ。
—
3. 実践:Strict Modeに怒られない「クリーンなコード」
現場でよくある「アンチパターン」と「修正案」を見てみよう。
【アンチパターン】クリーンアップ関数がない
外部APIを叩く際、クリーンアップ関数を忘れると、コンポーネントが破棄された後も非同期処理が走り続け、状態更新エラー(`Warning: Can’t perform a React state update on an unmounted component`)の原因になる。
// ❌ 悪い例:クリーンアップがない
useEffect(() => {
let isSubscribed = true;
fetchData().then((data) => {
if (isSubscribed) setData(data);
});
// Strict Modeの2回呼び出しで、前のリクエストが放置される可能性がある
}, []);
【ベストプラクティス】クリーンアップ関数で状態を管理する
以下のように、クリーンアップ関数でフラグを倒すのが鉄板だ。
import { useState, useEffect } from ‘react’;
const UserProfile = ({ userId }) => {
const [data, setData] = useState(null);
useEffect(() => {
// 1. フラグを用意
let ignore = false;
const fetchData = async () => {
const result = await fetchUser(userId);
// 2. フラグが立っていなければ状態を更新
if (!ignore) {
setData(result);
}
};
fetchData();
// 3. クリーンアップ関数:コンポーネントがアンマウント、または再レンダリング時に実行
return () => {
ignore = true; // 次のレンダリングや破棄時に「古い処理」を無視させる
};
}, [userId]); // userIdが変われば再実行される
return
;
};
—
4. 現場のシニアとしてのアドバイス
「開発中だけログが2回出るのが気持ち悪い」という理由で Strict Mode を外すチームがある。これは断言するが絶対にやってはいけない。
君が書いたコードは、君自身よりも賢いReactのエンジンによって「将来的にどう振る舞うか」をテストされている。もしログが2回出て困るような副作用があるなら、それは「冪等性(べきとうせい:何度実行しても同じ結果になること)」が担保されていない証拠だ。
プロのチェックリスト
- APIコール: `useEffect` 内での取得は `AbortController` やクリーンアップフラグを使っているか?
- 外部ライブラリ: `useEffect` 内で初期化しているライブラリは、正しく `destroy` を呼んでいるか?
- Refの利用: `useRef` を使ったDOM操作が、再レンダリング時に競合しないか?
最後に
Reactの Strict Mode は、君を苦しめる敵ではなく、「本番環境での予測不能なクラッシュ」を未然に防ぐための最強の味方だ。
もし開発中に不可解な挙動があったら、「Reactがまた僕にヒントをくれているんだな」とポジティブに捉えてみてほしい。その先には、より堅牢で、どんな環境でも安定して動作する美しいフロントエンドの世界が待っているはずだ。
明日からの開発で、ぜひこの「二重レンダリング」をデバッグの武器として使いこなしてみてくれ。応援しているぞ。

コメント