【実務・中級編】 useRefによるDOMアクセスと値の保持 – React実践ガイド

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 (

{/ 2. ref属性に紐付ける。Reactがレンダリング時にDOMを自動注入する /}

);
};

—

応用:レンダリングをトリガーしない「状態保持」

次に、実務でよくある「タイマー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らしい」洗練されたものになるはずだ。明日からの開発で、ぜひ意識してみてほしい。

何かあればまたいつでも聞きに来てくれ。現場からは以上だ。

コメント

タイトルとURLをコピーしました