概要
- Reactアプリケーションで、DOM要素へのアクセスやレンダー間での値保持を実現できる
useRef
フックを紹介
-
useState
と異なり、値を更新しても再レンダリングが発生しないため、タイマー管理や前回値の保持などに有効
-
フォーム制御、アニメーション制御、外部ライブラリ連携にも頻出する基本フック
useRef
フックによって 「過去の値を保存しつつ、UIに影響しない」 というUIを作成できる
実施条件
- React + TypeScript プロジェクトが構築済みであること
- React Hooksの基本(
useState
, useEffect
など)を理解していること
- DOMやイベントの基礎知識があること(フォーム、タイマー、描画など)
環境
ツール |
バージョン |
目的 |
Node.js |
22.5.1 |
Reactアプリ実行環境 |
React |
19.1.0 |
UI構築 |
TypeScript |
4.9 |
型定義による安全な開発 |
useRef<T>(初期値)
の役割
-
useRef
のジェネリクス <T>
は、ref.current
がどんな型の値を持つかをTypeScriptに伝える
useRef
の基本構造
- importセクション
- 型定義セクション
- 関数定義セクション
3.1 内部状態管理セクション
3.2 副作用(useEffect)処理セクション
3.3 返り値構築・ロジックセクション
基本構文: useRef
の使い方
// 1. importセクション
import React, { useRef, useEffect } from 'react';
// 2. 型定義セクション
// 今回は特に不要(DOM型は自動推論されるが、明示する場合はRefObject型を利用)
// 3. 関数定義セクション
const InputFocus = () => {
// 3.1 内部状態管理セクション
const inputRef = useRef<HTMLInputElement>(null);
// 3.2 副作用処理セクション
useEffect(() => {
inputRef.current?.focus(); // 初回マウント時に自動でフォーカス
}, []);
// 3.3 返り値構築・ロジックセクション
return (
<div style={{ padding: '1rem' }}>
<label>名前: </label>
<input ref={inputRef} type="text" placeholder="ここに入力" />
</div>
);
};
export default InputFocus;
inputRef = useRef<HTMLInputElement>(null)
と書くことで、TypeScriptが inputRef.current
の型を自動で認識できるようになる。
活用例
1. タイマー管理:setTimeout
を使って再レンダリングなしにタイマーを制御
// 1. importセクション
import React, { useRef, useEffect } from 'react';
// 2. 関数定義セクション
const TimerComponent = () => {
// 3.1 内部状態管理セクション
const timerRef = useRef<NodeJS.Timeout | null>(null);
// 3.2 副作用処理セクション
useEffect(() => {
timerRef.current = setTimeout(() => {
console.log('3秒後に実行されました');
}, 3000);
return () => {
if (timerRef.current) clearTimeout(timerRef.current);
};
}, []);
// 3.3 返り値構築・ロジックセクション
return <p>3秒後にコンソールにログが出力されます</p>;
};
export default TimerComponent;
timerRef = useRef<NodeJS.Timeout | null>(null)
と書くことで、TypeScriptが timeRef.current
の型を自動で認識できるようになる。
-
setTimeout
: 第1引数:遅延実行したい関数(コールバック)、第2引数:遅延時間(ミリ秒)
-
clearTimeout
: setTimeout
でセットしたタイマーをキャンセルする
2. フォーカス制御:ボタンクリックで任意のinputにフォーカスを当てる
// 1. importセクション
import React, { useRef } from 'react';
// 2. 関数定義セクション
const FocusOnClick = () => {
// 3.1 内部状態管理セクション
const inputRef = useRef<HTMLInputElement>(null);
// 3.3 返り値構築・ロジックセクション
const handleClick = () => {
inputRef.current?.focus();
};
return (
<div style={{ padding: '1rem' }}>
<input ref={inputRef} type="text" placeholder="ここにフォーカスされます" />
<button onClick={handleClick} style={{ marginLeft: '1rem' }}>
フォーカスする
</button>
</div>
);
};
export default FocusOnClick;
3. 前回値の保持:状態の前回値を useRef
に記録する
// 1. importセクション
import React, { useState, useEffect, useRef } from 'react';
// 2. 関数定義セクション
const PreviousValue = () => {
// 3.1 内部状態管理セクション
const [count, setCount] = useState(0);
const prevCountRef = useRef<number>(0);
// 3.2 副作用処理セクション
useEffect(() => {
prevCountRef.current = count;
}, [count]);
// 3.3 返り値構築・ロジックセクション
return (
<div style={{ padding: '1rem' }}>
<p>現在の値: {count}</p>
<p>前回の値: {prevCountRef.current}</p>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
</div>
);
};
export default PreviousValue;
prevCountRef = useRef<number>(0)
と書くことで、TypeScriptが prevCountRef.current
の型を自動で認識できるようになる。
-
count
が更新されるたびにこの副作用が走る
- つまり、次回の更新時には
prevCountRef.current
に「前回のcount
値」が入っている状態になる
useRef
の用途まとめ
ユースケース |
解説 |
DOMアクセス |
inputRef.current.focus() などで、直接DOMを操作可能 |
タイマー管理 |
setTimeout や setInterval のID保持に便利 |
状態の永続保持 |
レンダー間で変化する値を保持しつつ、再レンダリング不要 |
前回値の保持 |
useEffect と組み合わせて前回値を保持できる |
外部ライブラリ/キャンバスとの連携 |
初期化処理などに利用されるケースが多い |
HTMLタグおよびタイマーIDのTypeScript型対応表
種類 |
具体例 |
TypeScriptでの型名 |
用途例 |
HTMLタグ |
<div> |
HTMLDivElement |
DOMの操作(背景色、レイアウトなど) |
|
<button> |
HTMLButtonElement |
ボタンのクリック処理など |
|
<input> |
HTMLInputElement |
フォーム入力、フォーカス制御など |
タイマー管理 |
setTimeout() など |
NodeJS.Timeout (Node.js) |
タイマーID管理、clearTimeout() に使用 |
|
setTimeout() など |
number (ブラウザ) |
タイマーID管理、clearTimeout() に使用 |
参考リンク