React Hooks の useRef ってどんな機能があるの?
今回は、React Hooks の中でも重要な useRef について、基本的な考え方や使い方、代表的なユースケースまでをわかりやすく解説します。
useRef とは?
useRef とは、React.js で 値を保持するためのHookの一つ で、値を変更しても再レンダリングが発生しない という特徴があります。
また useState と同様に値を保持できますが、useRef は 画面の再描画が不要な値をコンポーネント内で管理したい 場合に使用されます。
useRef の基本構文
特徴
- useRef(初期値) の形で使用し、保持した値は ref.current を通して読み書きする
- ref.current の値を変更しても 再レンダリングは発生しない
- コンポーネントが再レンダリングされても 値は保持される
※ useRef で保持している値は、画面の表示には直接反映されません。
const ref = useRef(初期値);
console.log(ref.current); // 戻り値
useRef コード例
再レンダリング不要な値の保持
処理フロー
useRef(0)で初期値0を持つ ref(countRef) を作成- 値は
countRef.currentに保存され、ログに”レンダリング“が1回のみ表示される - ボタンをクリックするたびに値は増えるが、画面は再レンダリングされない
※の変更では再レンダリングされないcountRef.current
import { useRef } from "react";
export default function UseRefCounter() {
const countRef = useRef(0);
console.log("レンダリング");
const increment = () => {
countRef.current += 1;
console.log(countRef.current);
};
return (
<>
<h2>useRef Counter</h2>
<button onClick={increment}>+1</button>
</>
);
}

DOMへの参照
処理フロー
- useRef(null) を使って ref(inputRef) オブジェクトを作成する
- 作成した ref(inputRef) を JSX の input の ref 属性に渡す
- コンポーネントがマウントされ、DOMが生成される
- 対応する DOM 要素が ref.current に代入される
- ref.current を通して DOM を直接操作できる(focus、scrollIntoView など)
※ DOMが生成される前の ref.current は null のため、optional chaining(?.) が必要
注意点
React.js では、基本的に useState を使って UI を制御することが推奨されているため、DOMを直接操作する useRef は「どうしても必要な場合」に限定して使用するのが望ましいです。
import { useRef } from "react";
export default function UseRefFocusInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current?.focus();
};
return (
<>
<h2>useRef focusInput</h2>
<input ref={inputRef} />
<button onClick={focusInput}>フォーカス</button>
</>
);
}

useRef と useState との違い
基本的な違い
| 項目 | useState | useRef |
|---|---|---|
| 値の保持 | コンポーネントの状態として保持 | 値を保持するが状態には含まれない |
| 値の更新 | setState を使う | ref.current を直接変更 |
| 再レンダリング | 値を更新するとコンポーネントが再レンダリングされる | 値を変更しても再レンダリングされない |
| UI反映 | 変更した値は自動で画面に反映される | 画面には反映されない |
| 主な用途 | 画面表示に関わる状態の管理 | 再レンダリング不要の値の保持、DOM参照 |
使い分けるポイント
useState ※ 画面に影響する値
- UIに表示したい値
- ユーザー操作に応じて画面を更新したい場合
- 値の変更に伴い、再レンダリングが必要な場合
useRef ※ 画面に影響しない値
- 前回の値を保持して比較したいときや、初回レンダリングかどうかのフラグなど 再レンダリングが不要な値
- DOM要素を直接操作したい場合(例:
focus, scrollIntoView) - タイマーIDや WebSocket など、外部ライブラリの参照を保持したい場合
useRef のその他ユースケース
タイマーIDの管理
- タイマーID は timerRef.current に保持
- 値が変わっても再レンダリングされない
- タイマーID を覚えておくだけなので useStateは不要
import { useRef } from 'react';
export default function UseRefTimer() {
const timerRef = useRef(null);
const startTimer = () => {
if (timerRef.current !== null) return;
timerRef.current = window.setInterval(() => {
console.log('タイマー実行中 タイマーID:', timerRef.current);
}, 1000);
};
const stopTimer = () => {
if (timerRef.current !== null) {
console.log('タイマーストップ タイマーID:', timerRef.current);
clearInterval(timerRef.current);
timerRef.current = null;
}
};
return (
<>
<h2>useRef Timer</h2>
<button onClick={startTimer}>開始</button>
<button onClick={stopTimer}>停止</button>
</>
);
}

初回レンダリング判定のフラグ
- 初回レンダリング時は isFirstRender.current === true となり、ログに”初回レンダリング” と表示される
- 一度 false にすると、カウントのstateを+1ずつアップしても、ログに”2回目以降” と表示され、再レンダリングされずに以降も保持される
- useRef ではなく、useState にすると無駄に 再レンダリングが発生してしまう
import { useEffect, useRef, useState } from 'react';
export default function UseRefFirstRenderCounter() {
const isFirstRender = useRef(true);
const [count, setCount] = useState(0);
useEffect(() => {
if (isFirstRender.current) {
// 初回レンダリング時のみ実行
console.log('初回レンダリング');
isFirstRender.current = false;
return;
}
// 2回目以降のレンダリング時に実行
console.log('2回目以降');
});
return (
<>
<h2>useRef FirstRender Counter</h2>
<p>count: {count}</p>
<button onClick={() => setCount((prev) => prev + 1)}>
再レンダリング
</button>
</>
);
}

まとめ
React Hooks の useRef について、いかがでしたでしょうか?
useRef は、前回の値を保持して比較したいとき や DOM要素の直接操作、初回レンダリング判定のフラグ など、画面の描画には影響しない値をコンポーネント内で管理したい場合に適したHookです。
まずは useState との違いを意識しながら、基本的な使い方をしっかり押さえ、小さなサンプルで試しながら、少しずつ useRef に慣れていきましょう。

コメント