概要
-
Reactアプリケーションで、DOM要素へのアクセスやレンダー間での値保持を実現できる
useRefフックを紹介 -
useRefは本質的に次の2つの役割を持つ:- DOM 要素への参照を保持する
- 再レンダリングを引き起こさない値を保持する
-
useStateと異なり、値を更新しても再レンダリングが発生しない -
タイマー管理、前回値の保持、フォーム制御、外部ライブラリ連携などに有効
useRefフックによって
「DOMを安全に操作できる」かつ「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に伝える
| 役割 | 説明 |
|---|---|
| DOM 要素への参照を保持する |
ref.current に実際の DOM 要素が入る |
| 再レンダリングを引き起こさない値を保持する | 値を変更しても UI は再描画されない |
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 要素への参照を保持する | DOMアクセス |
focus(), click() など |
| 外部ライブラリ連携 | canvas / chart / map 初期化 | |
| 再レンダリングを引き起こさない値を保持する | タイマー管理 |
setTimeout 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() に使用 |