React:ref とは

DOM ノードにアクセスする時に使うのが ref

本来 React がレンダー結果に応じて DOM を自動更新するので、コンポーネントで DOM を操作することはないが、ノードへのフォーカスやサイズ・位置測定であったり、React が公開していないブラウザ API 呼び出しなどをしたいときに使える。

ブラウザ API 呼び出し時の注意点としては、フォーカスやスクロールなどの非破壊的なアクションであれば問題ない。一方で、React を介さずに DOM を書き換えるような操作には細心の注意が必要。

理由としては、React を介さずに直接 DOM を書き換えてしまうと、React は以降で正しくそれらを管理できなくなってしまうため。

よって、React が管理する DOM ノードを手動で変更する場合においては、React が更新しない部分のみとする。

ref の使い方

  1. useRef フックのインポート

     import { useRef } from 'react';
    
  2. コンポーネント内で宣言

     const myRef = useRef(null);
    
  3. 参照したい DOM ノードの ref 属性にオブジェクトを渡す

     <div ref={myRef}>
    
  4. 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.currentnull になっており、またレンダー中は DOM ノードが更新されていないため参照すべきでない。

  • コミット中に、React は DOM に変更を適用する

React が ref.current をセットするのはコミット中。React は DOM を更新する前に一度 ref.currentnull に設定する。

React は DOM を更新後に当該ノードの ref をセットするようになっている。よって、コミット中も値が確定しているわけではないので、参照すべきでない。

以上から、ref を参照すべきタイミングはイベントハンドラになる。

参考