第98章:練習:型を付けたuseRefで、ページを開いたら入力欄にカーソルを合わせる
この章のゴールはシンプルです 💪
ページを開いた瞬間に、指定した入力欄にカーソルが自動で入るフォーム を、型付き
useRefを使って作れるようになること 🎯
1. 今回やりたいことイメージ 💡
- 画面を開く(リロードする)➡ 何もクリックしてないのに…
- あるテキストボックスに、カーソル(キャレット)が最初から入っている ✨
- 「ログインフォーム」「検索ボックス」みたいな UX でよくあるやつです。
これを実現するためのポイントは3つです👇
useRefで 入力欄への参照(ref) を作るuseRef<HTMLInputElement | null>(null)のように 型をキッチリ付けるuseEffectで 初回表示のタイミングでfocus()を呼ぶ
React 19 では useRef に 必ず初期値(引数)を渡す書き方 が推奨されているので、null を入れておく形が自然です。(React)
2. 流れをざっくり図で見る 🌀
Mermaid で「何が先に起こるのか」をイメージしてみましょう 🌈
ざっくり言うと、
- まず 画面が描画されてから
- React が
refに DOM 要素を入れて - そのあと
useEffectが呼ばれてfocus()する
という順番です 💡
3. 新しいコンポーネントファイルを作ろう 📁
プロジェクトはすでに Vite + React + TS で動いている前提にします(前の章までで作ったやつ)🖥️
src フォルダ直下に、こんなファイルを作ります:
src/AutoFocusInput.tsx
中身をいったん丸ごと書いてみましょう ✍️
import { useEffect, useRef } from "react";
export const AutoFocusInput = () => {
// ★ 1) 型付きの ref を用意する
// HTMLInputElement | null 型の current を持つ ref
const inputRef = useRef<HTMLInputElement | null>(null);
// ★ 2) コンポーネントが「初めて」表示されたタイミングで focus する
useEffect(() => {
// current が null じゃないときだけ focus() を呼ぶ
inputRef.current?.focus();
}, []);
return (
<div style={{ padding: "1.5rem" }}>
<label
htmlFor="username"
style={{ display: "block", marginBottom: "0.5rem" }}
>
ユーザー名
</label>
<input
id="username"
ref={inputRef}
type="text"
placeholder="ここにカーソルがくるよ!"
style={{
padding: "0.5rem 0.75rem",
fontSize: "1rem",
borderRadius: "4px",
border: "1px solid #ccc",
width: "100%",
maxWidth: "320px",
}}
/>
<p style={{ marginTop: "0.75rem", fontSize: "0.9rem", color: "#555" }}>
ページをリロードして、最初からカーソルが当たっているか試してみてね ✨
</p>
</div>
);
};
4. それぞれのポイントを分解して理解しよう 🔍
4-1. 型付き useRef の書き方 🎯
const inputRef = useRef<HTMLInputElement | null>(null);
HTMLInputElement→ ブラウザの「<input>タグ」を表す TypeScript の型(DOM 型)です。(React)| null→ 最初のレンダリングの直後など、まだ DOM が入っていない瞬間 もあるので、nullも許可。null(引数) → React 19 + TypeScript ではuseRef()を 引数なしで呼ぶのは NG で、初期値を必ず渡すスタイルになっています。ここでは「最初は何もささってないよ」という意味でnullを渡しています。(React)
この1行で、
「この ref は、将来
HTMLInputElementかnullが入ります」
という情報を TypeScript に教えているイメージです 👩🏫
4-2. ref を input に渡す ✋
<input
id="username"
ref={inputRef}
type="text"
placeholder="ここにカーソルがくるよ!"
/>
-
ref={inputRef}と書くことで、- React がこの
<input>の 実物の DOM 要素 を inputRef.currentの中に入れてくれます。
- React がこの
value や onChange のような「通常の props」とはちょっと違う、「裏口のような特別ルート」 だと思っておくとイメージしやすいです 😎
4-3. 初回表示で focus() する useEffect ⏱️
useEffect(() => {
inputRef.current?.focus();
}, []);
ここも大事なポイントがいくつかあります 👇
[](依存配列が空) → 「初回表示のときに 1回だけ 実行してね」という意味。inputRef.current?.focus();→?.は「オプショナルチェーン」。currentがnullのときは何もせず、安全にスルーしてくれます。- なぜ
useEffectの中で呼ぶの? →focus()したいのは 「DOM が確実に存在してから」 だからです。 描画前に呼ぼうとしても、まだinputRef.currentに何も入っていません。(React)
5. App.tsx に組み込んで動かしてみよう 🚀
今度は App.tsx にこのコンポーネントを読み込んでみます。
src/App.tsx
import "./App.css";
import { AutoFocusInput } from "./AutoFocusInput";
export const App = () => {
return (
<main style={{ fontFamily: "system-ui, sans-serif", padding: "2rem" }}>
<h1 style={{ fontSize: "1.5rem", marginBottom: "1rem" }}>
第98章:useRef で自動フォーカス ✨
</h1>
<AutoFocusInput />
</main>
);
};
実行手順 ✅
-
ターミナルでプロジェクトのフォルダを開く
-
開発サーバーを起動
npm run dev -
ブラウザで表示されているページを開く(または更新する)
-
ページが読み込まれた瞬間に、 👉 入力欄にカーソルが入っていれば成功です 🎉
6. よくあるハマりポイント 🐛
❌ useRef() を引数なしで呼んでいる
// これは React 19 + TypeScript だとエラーになる
const inputRef = useRef();
- 「引数が足りません」みたいなエラーが出ます。
- かならず初期値を渡しましょう ✅
const inputRef = useRef<HTMLInputElement | null>(null);
❌ inputRef.current.focus() と書いてエラー
// エラー: Object is possibly 'null'.
inputRef.current.focus();
-
currentの型がHTMLInputElement | nullなので、- TypeScript 的には「null かもしれないでしょ?」と怒られます。
-
解決方法:
- オプショナルチェーン
?.を使う - もしくは
if文でnullチェックをする
- オプショナルチェーン
inputRef.current?.focus();
// または
if (inputRef.current !== null) {
inputRef.current.focus();
}
❌ useEffect を書いていない(or 依存配列がない)
// これだとレンダリングのたびに focus してしまう
useEffect(() => {
inputRef.current?.focus();
}); // ← [] を忘れている
-
入力している最中に、別の state 更新で再レンダリングが走ると…
- 毎回カーソルが先頭に戻る、みたいなイライラ挙動になります 😇
-
「最初の1回だけ」にしたいなら、必ず
[]を付ける ✅
7. ミニ練習問題で定着させよう ✍️
📝 練習1:パスワード入力欄にも自動フォーカス
-
AutoFocusInputをコピーして、AutoFocusPassword.tsxのようなコンポーネントを作る
-
type="password"の<input>にrefを付けて、 -
ページを開いたらパスワード欄に自動でフォーカスされるようにしてみよう。
👉 ポイント:
useRef<HTMLInputElement | null>(null) と useEffect のセットは同じです ✔️
📝 練習2:ボタンを押したら別の入力欄にフォーカス
AutoFocusInputを少し改造して、入力欄を 2つ 作る- 2つ目の入力欄用に、もうひとつ
useRefを用意する - ボタンを押したら、2つ目の入力欄に
focus()するようにしてみよう
ヒント:
const firstInputRef = useRef<HTMLInputElement | null>(null);
const secondInputRef = useRef<HTMLInputElement | null>(null);
const handleMoveFocus = () => {
secondInputRef.current?.focus();
};
8. この章のまとめ 🎀
-
useRefは DOM 要素を直接触りたいときの「窓口」 みたいなもの 🪟 -
React 19 + TypeScript では
useRefに 必ず初期値を渡す スタイルになるので、useRef<HTMLInputElement | null>(null)の形を覚えておくと安心 💡(React) -
初回表示で
focus()したいときは、refを input に付けるuseEffect(依存配列は[])の中でref.current?.focus()を呼ぶ
-
オプショナルチェーン
?.で null チェックも同時にできる のが便利 ✨
次の章では、この useRef をさらに応用して、
「前の値を覚えておく」「動画を再生・停止する」など、もう一歩進んだ使い方も見ていきます 🎬📦