第95章:useRef の使い方 (2)
1️⃣ 今日やること 🎯
この章では、
- HTMLタグ(DOM要素)に 直接さわるための
useRefの使い方 - 「
document.getElementByIdじゃダメなの?」をReact目線で理解する - ボタンを押したら、特定の場所までスクロールしたり、入力欄にフォーカスしたりする小さな例
をやります 💪
前の章までで、
useRefは「値を覚えておける箱📦(でも再レンダリングしない)」として使った- タイマーIDを保存する、という使い方を見た
今回はそれの 「DOM要素版」 です ✨
2️⃣ ref は「DOMへのリモコン」だと思おう 🎮
Reactの世界をざっくり図にすると、こんな感じです:
- いつも書いている
return ( ... )の中の JSX から ブラウザが本物の HTML要素(DOM)を作ってくれます 🧱 refを使うと、「そのDOM要素を指す、特別なオブジェクト」 をもらえます- そのオブジェクトが
myDivRef.currentです ✨
だから、
myDivRef.current.scrollIntoView(...)myInputRef.current.focus()myVideoRef.current.play()
みたいに、DOMのメソッドを直接呼び出せるようになります 🎮
3️⃣ 基本パターン:3ステップで覚えよう ✅
DOM要素に ref を使う基本形は、この3ステップです:
- コンポーネントの先頭で
useRefを用意する - JSXで
ref={...}を指定する - イベントの中で
ref.currentを使う
順番に見ていきます 👀
🧩 ステップ1:useRef を宣言する
import { useRef } from 'react';
function Example() {
const boxRef = useRef<HTMLDivElement | null>(null);
// ...
}
ポイント 👇
-
useRef<HTMLDivElement | null>(null)-
ここは 「このrefは
<div>を指す予定だよ」 という型の指定です -
nullになっているのは、まだDOMが作られていないタイミングがあるから -
型の意味は、第96章でガッツリ解説するので、今は
「
<div>用のおまじない」 くらいに思ってOKです 🙆♀️
-
🧩 ステップ2:JSXで ref をくっつける
return (
<div>
<div ref={boxRef}>
ここがターゲットのボックスです 🎯
</div>
</div>
);
ref={boxRef}と書くことで、 この<div>とboxRef.currentがリンクされます 🔗- Reactが「レンダリング完了したよ〜」となったタイミングで
boxRef.currentに 本物のDOM要素 が代入されます
🧩 ステップ3:イベントの中で ref.current を使う
const handleClick = () => {
if (boxRef.current) {
boxRef.current.scrollIntoView({ behavior: 'smooth' });
}
};
-
boxRef.currentがnullじゃないときだけ、メソッドを呼びます -
scrollIntoViewは、「この要素が画面に見える位置までスクロールして〜」 というブラウザのメソッドです 📜
4️⃣ ミニ実験①:ボタンで「ここまでスクロール」するアプリ 🧪
「ボタンを押したら、ずっと下の方にある箱までスーッとスクロールする」 という小さなコンポーネントを作ってみましょう ✨
src/ScrollToBox.tsx というファイルを作るイメージです。
import { useRef } from 'react';
export function ScrollToBox() {
// ① 下のピンクのボックスを指す ref
const boxRef = useRef<HTMLDivElement | null>(null);
// ② ボタンが押されたときの処理
const handleScrollClick = () => {
if (boxRef.current) {
boxRef.current.scrollIntoView({
behavior: 'smooth', // なめらかスクロール ✨
block: 'center', // 画面の真ん中あたりに来るように
});
}
};
return (
<div
style={{
height: '200vh', // わざと縦長ページにする
padding: '16px',
}}
>
<h1>useRef でスクロールしてみよう 🎀</h1>
<button
onClick={handleScrollClick}
style={{
padding: '8px 16px',
borderRadius: '999px',
border: 'none',
cursor: 'pointer',
}}
>
🎯 下のピンクのボックスまでスクロール
</button>
{/* 画面のだいぶ下にボックスを配置 */}
<div style={{ marginTop: '120vh' }}>
<div
ref={boxRef}
style={{
padding: '24px',
borderRadius: '16px',
backgroundColor: '#fce7f3',
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
}}
>
ここがターゲットのボックスだよ 🎯💗
</div>
</div>
</div>
);
}
使い方メモ 📝
App.tsxでこのコンポーネントを読み込めばOKです:
import { ScrollToBox } from './ScrollToBox';
function App() {
return <ScrollToBox />;
}
export default App;
ブラウザで試してみてください 👀
- ページを開くと上の方にボタンがある
- ボタンを押すと、画面がスーッと下にスクロールして
- ピンクのボックスが真ん中にドンッと出てくるはずです 🎉
「このボックスにスクロールしたいな」 →
refを付けるだけでOK という感覚をつかめればバッチリです 🙆♀️
5️⃣ ミニ実験②:ボタンで入力欄にカーソルを合わせる ✏️
次は、ボタンを押したら <input> にフォーカス する例です。
src/FocusInput.tsx を作るイメージでどうぞ 🌸
import { useRef } from 'react';
export function FocusInput() {
const inputRef = useRef<HTMLInputElement | null>(null);
const handleFocusClick = () => {
// ?. を付けると「current があれば実行してね」という書き方になるよ
inputRef.current?.focus();
};
return (
<div style={{ padding: '16px' }}>
<h2>ボタンでフォーカスしてみよう 👀</h2>
<input
ref={inputRef}
type="text"
placeholder="ボタンを押すとここにカーソルが来るよ ✏️"
style={{
padding: '8px',
borderRadius: '8px',
border: '1px solid #ddd',
width: '260px',
}}
/>
<div style={{ marginTop: '12px' }}>
<button
onClick={handleFocusClick}
style={{
padding: '6px 12px',
borderRadius: '999px',
border: 'none',
cursor: 'pointer',
}}
>
✨ 入力欄にカーソルを移動する ✨
</button>
</div>
</div>
);
}
これも App.tsx から呼び出せばOKです:
import { FocusInput } from './FocusInput';
function App() {
return <FocusInput />;
}
export default App;
6️⃣ document.getElementById じゃダメなの?🤔
ブラウザだけやっていたときは、
document.getElementById('...')document.querySelector('...')
をよく使っていましたよね 💡
Reactでは、基本的にそれはあまり推奨されません。理由は:
- Reactが「どのタイミングでDOMを作るか」「どう書き換えるか」を管理しているから
- 外から
document.getElementByIdでいじると、 Reactの考えている状態とズレることがある 🥲
代わりに:
- 「この要素だけ直接さわりたい」というときは
👉
refを使って React に任せたまま、必要なところだけ触る
というスタイルが、Reactっぽい書き方です 🌸
7️⃣ ref でやっていいこと / 避けたほうがいいこと ☯️
✅ やってOKなこと(一例)
- フォームの入力欄に
focus()を当てる - 特定のところまで
scrollIntoView()でスクロール <video>のplay()/pause()など、メディア操作- キャンバスや地図ライブラリなど、 「Reactの外のライブラリ」とつなぐとき
⚠️ ほどほどにしたいこと
ref.current.style.color = 'red';みたいな、 スタイル変更をごりごり直接やるやつ
これは 全部をrefでやり始めると、Reactを使う意味が薄れてしまう ので、
- 見た目の変更は、できるだけ「状態 (
useState) → JSX → CSS」でやる - 「どうしてもDOMのメソッドを呼びたいところ」だけ、
refを使う
というバランスがおすすめです 🌈
8️⃣ よくあるエラー&ひっかかりポイント 😵💫
❌ boxRef.current が null でエラーになる
原因あるある:
- まだDOMが作られていないタイミングで
.currentを触っている ref={boxRef}を書き忘れている
対策:
if (boxRef.current) { ... }やboxRef.current?.focus()のように、nullチェックを入れる
❌ Property 'focus' does not exist on type 'HTMLDivElement | null' 的な型エラー
-
第96章で「
useRefの型」の話をちゃんとやりますが、 型がnullを含むせいで、TSに怒られるパターンです ⚡ -
今は、
if (inputRef.current) { inputRef.current.focus(); }inputRef.current?.focus();のどちらかの形を覚えておけばOKです ✅
9️⃣ まとめ 🎀
この章のゴールをおさらいすると…
-
useRefは「ただの値」だけじゃなくて 👉 DOM要素(HTMLタグ)へのリモコン としても使える 🎮 -
使い方の3ステップは:
const boxRef = useRef<HTMLDivElement | null>(null);- JSXで
<div ref={boxRef}>...</div> - イベント内で
boxRef.current?.メソッド()を呼ぶ
-
document.getElementByIdの代わりに、refを使うのがReact流 💡 -
スクロールしたり、フォーカスしたり、メディアを再生したり… ちょっとした「魔法の一押し」ができるようになる ✨
次の 第96章 では、 今回チラッと出てきた
useRef<HTMLInputElement>(null)などの 型定義のちゃんとした意味
を、TypeScript目線でじっくりやっていきます 🧠💻
ここまでできたら、useRef とDOMの仲良し関係はだいぶイメージできているはずです 💕