🌐

【React】useRef() で初回の2回実行に対応する

2025/03/27に公開

背景

React の StrictMode って、開発環境で useEffect を2回実行しちゃうんですよね。
('react 純関数'とかでググれば中の話が出てきます)
最初は useState でフラグを管理してたんですが、「あれ? useRef のほうがよくね?」となったので、この記事を書きました。


useRef とは?

useRef は React のフックで、ざっくり言うとこんな感じで使えます。

  • DOM 要素への参照を保持ref 属性)
  • コンポーネント間で値を保持(再レンダリングなし)
  • useEffect の実行制御(今回のメインテーマ)

基本的な使い方

import { useRef } from "react";

function MyComponent() {
    const countRef = useRef(0); // 初期値 0

    const increment = () => {
        countRef.current += 1;
        console.log(countRef.current); // 更新されるけどレンダリングされない!
    };

    return <button onClick={increment}>Increment</button>;
}

useState とは違って useRef の値 (current) を更新しても コンポーネントは再レンダリングされません


useEffect の2回実行問題 in StrictMode

React の StrictMode では、開発環境 (NODE_ENV=development) で useEffect の副作用が 2回実行 される仕様になっています。(本番環境では1回だけ)

例えば、こんなコード↓

useEffect(() => {
    console.log("Effect 実行!");
    sendLoginRequest(); // 2回実行されるかも?
}, []);

これ、本番なら問題ないけど、開発中は2回実行されちゃう


useRef で StrictMode の2回実行を防ぐ!

この問題を回避するには useRef でフラグを作ってやる のがベスト!

const strictModeIgnoreRef = useRef(false);

useEffect(() => {
    if (strictModeIgnoreRef.current) return; // 2回目を防ぐ
    strictModeIgnoreRef.current = true;
    sendLoginRequest(); // これで1回だけ実行!
}, []);

なぜ useRef を使うのか?

useState を使うと、値を更新するたびに 再レンダリングが発生 しちゃうんですよね。でも useRef なら 値を保持しつつ、再レンダリングを防ぐ ことができる!


まとめ

フック 再レンダリングする? こんなときに使う!
useState する UI に影響を与える値(カウンター、フォーム入力)
useRef しない DOM 参照、useEffect の実行制御、一時的なフラグ管理

useEffect の2回実行問題に悩んでいるなら、useRef を試してみたら幸せになれるかも。

Discussion