DOM ノードにアクセスする時に使うのが ref
。
本来 React がレンダー結果に応じて DOM を自動更新するので、コンポーネントで DOM を操作することはないが、ノードへのフォーカスやサイズ・位置測定であったり、React が公開していないブラウザ API 呼び出しなどをしたいときに使える。
ブラウザ API 呼び出し時の注意点としては、フォーカスやスクロールなどの非破壊的なアクションであれば問題ない。一方で、React を介さずに DOM を書き換えるような操作には細心の注意が必要。
理由としては、React を介さずに直接 DOM を書き換えてしまうと、React は以降で正しくそれらを管理できなくなってしまうため。
よって、React が管理する DOM ノードを手動で変更する場合においては、React が更新しない部分のみとする。
ref の使い方
useRef
フックのインポートimport { useRef } from 'react';
コンポーネント内で宣言
const myRef = useRef(null);
参照したい DOM ノードの
ref
属性にオブジェクトを渡す<div ref={myRef}>
React が
<div>
に対応する DOM ノード作成後に組み込みブラウザ API を使用するmyRef.current.scrollIntoView();
ポイントだなと思ったのは、ref
属性を設定するのは非破壊的なアクションを実行したい DOM ノードで、何をするかはイベントハンドラで設定する:
import { useRef } from "react"; export default function Page() { const ref = useRef(null); return ( <> <nav> {/* イベントハンドラで ref 属性を設定した DOM ノードに対して実行したい組み込みブラウザ API を呼び出す */} <button onClick={() => ref.current.focus()}>Search</button> </nav> {/* 非破壊的なアクション(フォーカス)を実行したい DOM ノードに対して ref 属性を設定 */} <input placeholder="Looking for something?" ref={ref} /> </> ); }
ref を参照すべきでないタイミング
Reactでの更新は2つのフェーズからなり、更新タイミングでは ref
を参照すべきでない。
- レンダー中に、React はコンポーネントを呼び出して画面に表示される内容を決定する
レンダー中は DOM がまだ作成されていないため ref.current
は null
になっており、またレンダー中は DOM ノードが更新されていないため参照すべきでない。
- コミット中に、React は DOM に変更を適用する
React が ref.current
をセットするのはコミット中。React は DOM を更新する前に一度 ref.current
を null
に設定する。
React は DOM を更新後に当該ノードの ref
をセットするようになっている。よって、コミット中も値が確定しているわけではないので、参照すべきでない。
以上から、ref
を参照すべきタイミングはイベントハンドラになる。