Blog Article
React の useRef とは何か? state と違う「再レンダーしない値」の正体

はじめに
React を学んでいると、useState や useEffect と並んで useRef という Hook を目にします。しかし、useRef は初見では役割が非常にわかりにくく、「いつ使うのか」「何のためにあるのか」が掴みにくい Hook です。
useState は「画面を更新するための値」、useEffect は「副作用を扱うための仕組み」と比較的わかりやすいですが、useRef は「値を保持する」と説明されても、state と何が違うのかがはっきりしません。
この記事では、useRef の本質を初学者にも理解できるように、なぜ必要なのか、いつ使うのか、これがないとどう困るのか、そして実際のコードを通して丁寧に解説します。
参考にしたリンク
https://react.dev/reference/react/useRef
https://react.dev/learn/referencing-values-with-refs
“Refs are like state variables that don’t trigger re-renders when you set them.”
この一文が、useRef の本質を非常によく表しています。
useRef とは何か?
useRef を一言で言うなら、再レンダーを発生させずに値を保持できる入れ物です。
React のコンポーネントは、state が変わると再レンダーされます。しかし、すべての値の変更が画面更新につながるわけではありません。画面とは無関係な値まで state で管理すると、無駄な再レンダーが発生してしまいます。
そこで登場するのが useRef です。useRef は値を保持できますが、その値を変更しても再レンダーは起きません。
この特徴こそが useRef が存在する理由です。
なぜ state ではなく useRef が必要なのか
React の設計思想は、「 UI は state の結果である 」という考え方です。そのため、画面に影響する値は state で管理する必要があります。
しかし、アプリには次のような値も存在します。
- 前回の値を覚えておきたい
- タイマー ID を保存したい
- DOM 要素に直接アクセスしたい
- スクロール位置を保持したい
これらは画面表示とは直接関係ありません。このような値を state に入れてしまうと、値が変わるたびに再レンダーが発生し、パフォーマンスが悪化します。
useRef は、この「画面と関係ないが保持したい値」を扱うために存在します。
もし useRef がなかった場合、これらをグローバル変数やクロージャで管理することになり、React の管理下から外れてしまいます。結果として、予期せぬバグの原因になります。
useRef の基本的な使い方
useRef は次のように使います。
const ref = useRef(初期値)この ref は、次のような構造を持っています。
{
current: 初期値
}値は ref.current に保存されます。
ref.current = 新しい値この代入をしても、コンポーネントは再レンダーされません。
ここが state との決定的な違いです。
useRef と state の違いを理解する
state は「画面を更新するための値」、useRef は「画面とは関係なく保持する値」です。
例えばカウンターを作る場合、state を使います。
const [count, setCount] = useState(0)count が変わると画面が更新される必要があるからです。
一方で、クリック回数を裏で記録するだけなら、useRef で十分です。
const clickCount = useRef(0)
function handleClick() {
clickCount.current++
}この値は増え続けますが、画面は更新されません。
DOM に直接アクセスするための useRef
useRef の代表的な使い方が、DOM 要素への直接アクセスです。
React は基本的に DOM を直接触ることを推奨していませんが、フォーカスを当てる、スクロールするなどの操作は必要になります。
const inputRef = useRef(null)
useEffect(() => {
inputRef.current.focus()
}, [])
return <input ref={inputRef} />このように、ref を JSX の ref 属性に渡すことで、実際の DOM 要素にアクセスできます。
これは useRef の非常に重要な役割です。
前回の値を保持する useRef
useRef は、前回の値を覚えておく用途にもよく使われます。
const prevCount = useRef(0)
useEffect(() => {
prevCount.current = count
}, [count])これにより、「前回の count 」を保持できます。state ではこれを実現するのは難しくなります。
タイマーや ID を保持する useRef
setInterval や setTimeout の ID を保持する場合も useRef が使われます。
const timerRef = useRef(null)
function startTimer() {
timerRef.current = setInterval(() => {
console.log("tick")
}, 1000)
}
function stopTimer() {
clearInterval(timerRef.current)
}この ID は画面表示とは無関係なため、state で管理すべきではありません。
useRef はなぜ再レンダーしないのか
React は state の変更を検知して再レンダーしますが、useRef は単なるオブジェクトです。
React は ref.current の変更を監視していません。そのため、値が変わっても再レンダーは発生しません。
これは欠点ではなく、useRef の最大の特徴です。
いつ useRef を使うべきか
useRef の重要性を実感するのは、次のような場面です。
- DOM を操作するとき
- 前回の値を保持したいとき
- 再レンダーさせたくない値を持ちたいとき
- タイマーや外部 API の ID を管理するとき
これらはすべて、state では不適切なケースです。
useRef がないとどう困るか
useRef がなければ、開発者はこれらの値をコンポーネント外で管理することになります。すると、React のライフサイクルとずれてしまい、予期せぬタイミングで値が失われたり、バグが発生したりします。
useRef は、React の管理下で安全に「画面に関係ない値」を扱うための仕組みです。
具体例で整理する
useRef を使うケース
- input にフォーカスを当てる
- スクロール位置を保持する
- 前回の値を記録する
- タイマー ID を保持する
state を使うケース
- 画面に表示する値
- UI の変化に影響する値
state と useRef の違い
項目 | state | useRef |
|---|---|---|
再レンダー | 発生する | 発生しない |
主な用途 | UI の更新 | 値の保持 |
DOM 参照 | 不可 | 可能 |
前回値の保持 | やや難しい | 容易 |
まとめ
useRef とは、再レンダーを発生させずに値を保持できる仕組みです。
React において、すべての値を state で管理するべきではありません。画面と関係ない値は useRef に任せることで、パフォーマンスと設計の両方が改善されます。
ポイント整理
useRefは再レンダーしない値の入れ物- DOM へのアクセスに使う
- 前回の値やタイマー ID を保持する
- state と役割が明確に異なる
useRef の役割を理解すると、React の設計思想がよりはっきりと見えてきます。