🙌

useRefでフラグを保持する

に公開

React フロントでは useRef をフラグ代わりに使う場面が多いので、その振る舞いをサンプルで整理しておきます。

useRef とは?

  • React が提供する "箱"。useRef(initialValue) で箱を作ると、ref.current に好きな値を入れられる。
  • コンポーネントが何度レンダー(描画)されても、同じ箱が再利用される。だから値がリセットされない。
  • 値を変えても画面が再レンダーされない。副作用のフラグや最新 DOM の参照に向いている。

レンダーとは?

React コンポーネントの関数が呼ばれ、JSX から DOM を組み立て直すこと。setState や親から受け取る props が変わるとレンダーが起きて、関数本体が再実行される。useRef はその再実行に巻き込まれないので値を保持できる。

サンプルで見る useRef

import { useRef, useState } from "react";

export function SubmitButton() {
  const isSubmittingRef = useRef(false);
  const [result, setResult] = useState("未送信");

  const handleClick = async () => {
    if (isSubmittingRef.current) {
      return; //ボタン連打を効かなくしてる
    }

    isSubmittingRef.current = true; // フラグ ON
    setResult("送信中...");

    await fakeRequest(); 
    //...1.5秒後
    setResult("完了!");
    isSubmittingRef.current = false; // フラグ OFF
  };

  return (
    <button onClick={handleClick} disabled={isSubmittingRef.current}>
      {result}
    </button>
  );
}

async function fakeRequest() {
  return new Promise((resolve) => setTimeout(resolve, 1500));
}
  • isSubmittingRef はレンダーをまたいでも同じオブジェクト。setResult で再レンダーが走っても、isSubmittingRef.current の真偽値は保持される。
  • disabled 属性には ref の値をそのまま渡しているけれど、setResult による再レンダーで最新値が UI に反映される。

ref.current で何を参照できる?

  • 自前のフラグや最新の値(例:外部 API のレスポンスキャッシュ)
  • DOM 要素
  • ID など再レンダーで消したくない値

フラグで使うときの注意

  1. 画面更新が必要なら state と組み合わせる
    ref だけ更新しても UI は変わらない。上の例のように、見た目を変えるときは useState や親コンポーネントの props で描画を司る。
  2. 同じ ref を複数コンポーネントで共有しない
    コンポーネント外の変数に置いてしまうと、インスタンス間で値が混ざる。必要ならカスタムフックで包んでスコープを閉じる。
  3. 同期タイミングを意識する
    setTimeout 内で ref を読むときは、最新レンダーで更新した値が入っているか確認。useEffect のクリーンアップで OFF にしておくと安全。

useRef は「レンダーではリセットされないけれど、更新しても再レンダーを引き起こさない」ユニークな箱。フラグ管理や外部リソースの参照先として使うと、余計な state や再レンダーを増やさずに済むのでおすすめです。

Discussion