ReactのuseRefはDOM操作や再レンダー不要な値を保持する際に便利なHookです。しかし、最近では、「React useRef 使い方 非推奨」というキーワードで検索されるように、特定の場面では非推奨とされる使い方もあります。この記事では、useRefの正しい使い方、stateとの違い、非推奨なパターン、Lintルールとの関係まで整理し、実践的な理解が得られるように解説します。
目次
React useRef 使い方 非推奨なケースと基本原則
まずは、「React useRef 使い方 非推奨」という観点で、どのようなケースが非推奨とされるか、その基本原則を確認します。useRefの役割や本質を理解し、何故非推奨と言われるのかを把握することが、適切な使い分けの鍵になります。
useRefの本来の目的
useRefは、レンダリングに直接関係しない値を扱いたいとき、またDOMノードを参照したいときに使われます。DOM要素へのフォーカス、スクロール制御、外部ライブラリとの連携など、命令的操作が必要な場面が代表例です。レンダーに影響を及ぼすUI状態の管理には使われず、純粋なJavaScriptオブジェクトと値の保持が目的となります。useRefによる変更はレンダーを引き起こさないため、UIに表示すべき値には向きませんし、そのような場合はuseStateを使うべきです。
レンダー不可の値との比較(useStateとの違い)
useStateは状態が変わるとコンポ―ネントのリレンダリングを促しますが、useRefはcurrentプロパティの変更ではリレンダーを起こしません。StateはUI表示に関わる値の管理に使い、Refはそれとは逆に表示とは関係ないが挙動に影響を及ぼす非可視なデータの保存に使われます。レンダー回数のカウント、タイマーIDや前回のprops値など、レンダー毎に初期化されたくない値を使う場面でRefは重宝します。
非推奨な使い方とは何か
非推奨とされる使い方には以下のようなものがあります:
- UI表示に関係する値をuseRefで管理し、レンダーが必要なのにRefの変更に依存するケース
- useRefのcurrentをレンダー中に読み書きすること。これにより純粋関数としてのコンポーネントの期待が壊され、挙動が予測不能になる
- stateやpropsに依存するイベントハンドラやエフェクト内でuseRefを乱用し、依存性配列と同期が取れていないケース
これらはReactの公式ドキュメントやLintルールで警告されることが多く、最新の開発環境では非推奨とされるパターンと見なされます。
React useRef の具体的な使いどき:許容されるパターン
非推奨とされるものがある一方で、useRefは多くの場面で有効です。ここでは許容される具体的なパターンを整理します。これらを理解することで、何を使うべきかの判断力が身につきます。
DOM操作のための参照
テキスト入力フォームへの自動フォーカス、スクロール先の制御、アニメーション対象の要素の取得など、DOM要素に直接アクセスする必要があるケースではuseRefが有効です。この用途ではレンダー外での操作になるため、UI表示の妨げにならず、明示的に必要な場合に限って使われるべきです。
レンダー不要な値の保存
タイマーIDや外部APIとの購読状態、前回のpropsやstate値など、表示には使われないが、処理の中で参照したい値を保存する用途があります。こうした非表示の状態はuseRefで管理することで不要な再描画を避け、パフォーマンスの低下を防げます。
閉包や外部ライブラリとの統合
イベントハンドラやタイマー、CanvasやD3等外部ライブラリ使用時に、過去の値や関数参照が古くなる閉包問題を避けたい場合があります。最新の関数や状態へのアクセスを確保するためにuseRefを利用するパターンです。外部ライブラリからのDOM参照を渡すときやImperative Handleなどにも用いられます。
非推奨とされる具体的なケースとLintルール
次に、どのような使い方が具体的に非推奨とされているか、Lintルールとの関係を含めて整理します。最新情報を前提とし、警告が出るパターンや回避策を知ることが重要です。
レンダー中にref.currentを読み書きする
レンダー関数内部でref.currentの読み書きを行うことは、予測不能な挙動を引き起こすため避けるべきです。公式ドキュメントには、レンダー中の書き込みや読み込みは避けるよう明示されています。読み書きはイベントハンドラやuseEffect内でするべきで、これによってコンポーネントの純粋性を保ちます。
UIに表示すべき値をuseRefで管理する
画面上に表示されるべき値(たとえばボタンクリック数、フォームの入力内容など)は、stateで管理するべきであり、useRefでそれを管理するのは非推奨です。Refのcurrentを変更してもUIには反映されないため、直感的でないバグの原因となります。
useRefの初期値にstate値を直接渡すパターン
stateやpropsの値をuseRefの初期値としてそのまま渡し、その後ref.currentをそれと同期させるコードはLintルールで警告されることがあります。初期化値にstateを使うと、hookの引数にmutableな値を渡すことになるため「hooksの引数として渡された値を後で変更することは好ましくない」と判断されます。対処法としては初期値をnullなどにし、useEffectで同期させるパターンが推奨されます。
Lintルール “refs” とその他の警告
ReactのLintプラグインには、refsの取り扱いを検証するルールがあります。たとえば、レンダー中のref.current読み書きや、表示に使われる値をrefで管理する,createRefの使用を関数コンポーネントで避けるべきというルールなどです。これらは最新のLint環境でしばしばデフォルトで有効になっています。Lintの警告を無視すると保守性や将来的な互換性に問題が出ることがあります。
使い方のベストプラクティスと代替案
非推奨パターンを避け、正しく使うためのベストプラクティスおよび代替手段をまとめます。読み手が安全で可読性の高いReactコードを書けるようになることが目的です。
初期値は安定した値で null や定数を使う
useRefを初期化する際にはstate値やprops値を直接渡すのではなく、nullや定数を渡すのが望ましいです。その後useEffectで必要な値をref.currentに代入することで同期を取ります。これによりLintによる警告を防ぎ、意図が明確なコードになります。
state と useRef の使い分けを明確にする
UI表示やrenderへの影響が必要な値はuseStateで管理し、表示に関係しない値はuseRefで扱う、といったルールをプロジェクト内で明文化すると混乱が減ります。設計段階でどの値がUIに影響するかを考えて選択することが重要です。
命令的操作は最小限に抑える
Reactは宣言的UIが基本であり、それを壊す命令的操作は慎重に行う必要があります。スクロール、フォーカス、アニメーション操作などが必要な領域だけにuseRefを使い、それ以外はPropsやState、コンテキストなどで処理するようにします。命令的処理が増えるとコードの可読性やデバッグ性が低下します。
代替パターンの利用:state / useMemo / contextなど
UI表示に関係する値や頻繁に変わる値の場合はuseStateを使うべきです。また計算コストの高い処理にはuseMemoを使い、関数や値の安定化にはuseCallbackを使います。コンポーネント間で値を共有する必要があるならContext APIなどが適切です。これらを組み合わせることで、useRefの乱用を回避できます。
最新のReactでの変更点と将来への見通し
ReactやLintツールの進化に伴い、useRefに関する取り扱いもアップデートされています。現状の最新情報を押さえておけば、将来の互換性やチームでのコーディング規約策定にも役立ちます。
Strict Modeでの動作と開発時の副作用
Strict Modeではコンポーネント関数が開発時に2回呼ばれることがあります。その際、useRefも2回初期化されますが、片方は破棄されます。本番環境ではこの重複はないので、主に開発中の警告検出のための動きです。useRefが初期値を再評価するパターン(特に関数呼び出しを含む初期化)は注意が必要です。
Lintルールの強化と警告パターン
最新のLint環境では、useRefの非推奨な使い方に関する警告が強化されています。たとえばcreateRefの関数コンポーネント内での使用禁止、useRef初期値にstateを使うときの警告、レンダー中のcurrent読み書きなどが自動的に検出されます。これらはエラー扱いになることもあり、CI環境での統一が進んでいます。
将来予想されるReactでの変更
現時点でuseRef自体が廃止されるという話はありません。むしろReactチームはuseRefの使いどころを文書で明確にし、Lintツールで非適切なパターンに対する警告を増やす方向に進んでいます。宣言的UIとの整合性を重視し、命令的操作や副作用の場所を限定することでコード品質を高める方向性が強くなっています。
実践例:正しい使い方と非推奨パターンの比較
ここでは、具体的なコード例を通じて、正しいパターンと非推奨パターンを比較しながら解説します。実際の開発で役立つ指針となるように心がけます。
非推奨パターンの例
例として、UI上に表示したいクリック数をuseRefで管理し、それを直接JSXに表示しようとするパターンがあります:
function MyComponent(){
const clickCountRef = useRef(0);
const handleClick = () =>{
clickCountRef.current += 1;
};
return (クリック数:{clickCountRef.current});
}
このコードは、clickCountRef.currentの変更ではレンダーが再実行されないため、画面上の数値が更新されません。表示される値に関する管理にはuseStateが適切です。
正しいパターンの例
上記と同じ機能を実現する正しい例:
function MyComponent(){
const [count, setCount] = useState(0);
const clickCountRef = useRef(0);
const handleClick = () =>{
setCount(count + 1);
clickCountRef.current += 1; // ロジック用の値として保持
};
return (クリック数:{count});
}
この例ではuseStateがレンダーされる表示値を管理し、useRefは過去の値やロジック用の一時的な値として利用されています。このように役割を分けることで期待通りのUIと内部ロジックが両立します。
まとめ
React useRefは非常に役立つHookであり、DOM参照やレンダー不要な値の保存、外部ライブラリとの統合などに欠かせません。しかし、「React useRef 使い方 非推奨」という指摘があるように、状態表示に使ったり、レンダー中にcurrentを読み書きしたり、初期値にstateを不用意に与えるなどのパターンは避けるべきです。Lintルールもこれらの非適切な使い方を警告するよう強化されています。常にstateとの違いを意識し、命令的な操作を最小限に抑え、読みやすく保守性の高いコードを書くことが重要です。正しい使い方と避けるべきパターンを理解すれば、Reactでのコンポーネント設計がさらに安定し、信頼性の高いアプリケーション開発ができるようになります。
コメント