Reactの「禁じ手」を「最強の武器」に変える:useRefの深淵と実務的運用
やあ。現場でコードを書いていると、「Reactの流儀(宣言的UI)」に縛られすぎて、「ここだけは泥臭くやりたい!」という瞬間が必ず来るはずだ。
例えば、フォーカス操作、スクロール位置の制御、あるいはレンダリングをトリガーしたくないデータの持ち回り。そんな時、多くのエンジニアが「どうやってstateに詰め込もうか」と悩む。だが、そこで安易に`useState`を使うのは、パフォーマンスという観点では少しばかり遠回りだ。
今日はReactにおける「逃げ道」であり「最適化の切り札」である`useRef`について、現場の知見を詰め込んで解説する。
—
useRefの本質:レンダリングの輪廻からの脱却
まず、`useRef`の正体を明確にしておこう。これは単なる「DOMへの参照」ではない。「コンポーネントのライフサイクルを通して、値を保持し続けられる、Reactの監視対象外の箱」だ。
`useState`が「値が変われば画面を再描画する」というReactの契約であるのに対し、`useRef`は「値を変えても何もしない」という、いわば裏口だ。
なぜ「監視対象外」なのか
ブラウザの裏側では、Reactは`useState`や`props`が変更されるたびにコンポーネント関数を再実行し、仮想DOMの差分を計算する。しかし、`useRef`で保持したオブジェクト(`{ current: … }`)は、コンポーネントが再レンダリングされても同じメモリ領域を指し続ける。だからこそ、ここを書き換えてもReactは「おっと、画面更新は不要だな」と判断し、無駄な再レンダリングをスキップしてくれるんだ。
—
実践:DOM操作の作法
DOM操作を行う際、初心者が陥りがちなミスは「`document.getElementById`を直打ちする」ことだ。これはReactの仮想DOMとDOMの整合性を破壊する、現場では即NGが出るやり方だ。
以下は、入力フォームへプログラムからフォーカスを当てる、最もクリーンな実装パターンだ。
import { useRef } from ‘react’;
const AutoFocusInput = () => {
// 1. refオブジェクトを作成。初期値はnullでOK
const inputEl = useRef(null);
const handleFocus = () => {
// 3. currentプロパティ経由でDOM要素にアクセス
// 存在チェックを行うのが、予期せぬエラーを防ぐプロの作法
if (inputEl.current) {
inputEl.current.focus();
}
};
return (
);
};
—
応用:レンダリングをトリガーしない「状態保持」
次に、実務でよくある「タイマーID」や「直前のpropsの値」を保持するパターンを紹介しよう。これも`useState`でやると無限ループの温床になるが、`useRef`なら安全だ。
import { useRef, useEffect } from ‘react’;
const TimerComponent = () => {
// レンダリングには不要だが、処理には必要なタイマーIDを保持
const timerRef = useRef(null);
useEffect(() => {
timerRef.current = setInterval(() => {
console.log(‘裏で動いてるよ’);
}, 1000);
// クリーンアップ関数で確実に破棄する。これもref管理の鉄則
return () => clearInterval(timerRef.current);
}, []);
return
;
};
—
シニアからの「現場の心得」
最後に、`useRef`を使う上で一つだけ心に留めておいてほしいことがある。
「`useRef`の中身を、レンダリング中のコード(コンポーネントのreturnより前)で直接読み書きしないこと」だ。
Reactのライフサイクルにおいて、`ref.current`の読み書きは`useEffect`やイベントハンドラの中で行うのがルールだ。なぜなら、Reactはレンダリング中に副作用(Side Effect)が起きることを嫌う。レンダリング中に`ref.current`をいじると、Reactの並行レンダリングの仕組みと衝突し、デバッグ困難なバグを引き起こす可能性があるからだ。
- DOM操作が必要? → `useRef`を迷わず使え。
- 値の変更で画面を変えたい? → `useState`を使え。
- 画面には出ないが、処理上必要な値がある? → `useRef`で保持して、レンダリング負荷を下げろ。
この使い分けができるようになれば、君のコードはグッと「Reactらしい」洗練されたものになるはずだ。明日からの開発で、ぜひ意識してみてほしい。
何かあればまたいつでも聞きに来てくれ。現場からは以上だ。

コメント