useRef は、レンダリングには不要な値を参照できる React フックです。

const ref = useRef(initialValue)

リファレンス

useRef(initialValue)

コンポーネントのトップレベルで useRef を呼び出して、refを宣言します。

import { useRef } from 'react';

function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...

詳細な例については、以下をご覧ください。

パラメータ

  • initialValue:ref オブジェクトの current プロパティを最初に設定したい値。任意の型の値を指定できます。この引数は最初のレンダリング後に無視されます。

戻り値

useRef は、単一のプロパティを持つオブジェクトを返します。

  • current:最初は、渡した initialValue に設定されます。後で別の値に設定できます。JSX ノードの ref 属性として ref オブジェクトを React に渡すと、React はその current プロパティを設定します。

次のレンダリングでは、useRef は同じオブジェクトを返します。

注意点

  • ref.current プロパティは変更できます。状態とは異なり、変更可能です。ただし、レンダリングに使用されるオブジェクト(例えば、状態の一部)を保持している場合は、そのオブジェクトを変更しないでください。
  • ref.current プロパティを変更しても、React はコンポーネントを再レンダリングしません。ref は単なる JavaScript オブジェクトであるため、React は変更を認識しません。
  • 初期化を除き、レンダリング中に ref.current を書き込んだり読み込んだりしないでください。これにより、コンポーネントの動作が予測不能になります。
  • 厳格モードでは、React は 不純な処理が偶然発生するのを防ぐために、コンポーネント関数を2回呼び出します。これは開発時のみの動作であり、本番環境には影響しません。各 ref オブジェクトは2回作成されますが、バージョンのうち1つは破棄されます。コンポーネント関数が(そうあるべきように)純粋であれば、これは動作に影響を与えません。

使い方

ref を使って値を参照する

コンポーネントのトップレベルで useRef を呼び出して、1つ以上のrefを宣言します。

import { useRef } from 'react';

function Stopwatch() {
const intervalRef = useRef(0);
// ...

useRef は、単一の ref オブジェクト を返し、その初期値は、提供した 初期値 に設定された単一の current プロパティを持ちます。

次のレンダーでは、useRef は同じオブジェクトを返します。その current プロパティを変更して情報を格納し、後で読み取ることができます。これは state を思い出すかもしれませんが、重要な違いがあります。

ref を変更しても再レンダーはトリガーされません。これは、ref がコンポーネントの視覚的な出力に影響を与えない情報を格納するのに最適であることを意味します。たとえば、インターバル ID を格納して後で取得する必要がある場合は、ref に入れることができます。ref 内の値を更新するには、その current プロパティ を手動で変更する必要があります

function handleStartClick() {
const intervalId = setInterval(() => {
// ...
}, 1000);
intervalRef.current = intervalId;
}

後で、そのインターバル ID を ref から読み取って、そのインターバルをクリアできます

function handleStopClick() {
const intervalId = intervalRef.current;
clearInterval(intervalId);
}

ref を使用することで、次のことが保証されます。

  • 再レンダー間で情報を保存できます(すべてのレンダーでリセットされる通常の変数とは異なります)。
  • 変更しても再レンダーはトリガーされません(再レンダーをトリガーするステート変数とは異なります)。
  • 情報はローカルに各コンポーネントのコピーに限定されます(共有される外部の変数とは異なります)。

ref を変更しても再レンダーはトリガーされないため、画面に表示したい情報を格納するのには ref は適切ではありません。代わりに state を使用してください。useRefuseState のどちらを選ぶべきかについて詳しくはこちらをご覧ください。

useRef で値を参照する例

1 2:
クリックカウンター

このコンポーネントは、ボタンがクリックされた回数を追跡するために ref を使用します。ここでのクリック回数はイベントハンドラでのみ読み書きされるため、state の代わりに ref を使用しても問題ないことに注意してください。

import { useRef } from 'react';

export default function Counter() {
  let ref = useRef(0);

  function handleClick() {
    ref.current = ref.current + 1;
    alert('You clicked ' + ref.current + ' times!');
  }

  return (
    <button onClick={handleClick}>
      Click me!
    </button>
  );
}

JSX に {ref.current} を表示すると、クリックしても数値は更新されません。これは、ref.current を設定しても再レンダーがトリガーされないためです。レンダーに使用する情報は代わりに state である必要があります。

落とし穴

レンダー中に ref.current を書き込みまたは読み込みしないでください。

React は、コンポーネントの本体が純粋な関数のように振る舞うことを期待しています

  • 入力(propsstate、およびコンテキスト)が同じ場合、まったく同じ JSX を返す必要があります。
  • 異なる順序で呼び出したり、異なる引数を指定して呼び出したりしても、他の呼び出しの結果に影響を与えてはなりません。

レンダー中に ref を読み書きすると、これらの期待が損なわれます

function MyComponent() {
// ...
// 🚩 Don't write a ref during rendering
myRef.current = 123;
// ...
// 🚩 Don't read a ref during rendering
return <h1>{myOtherRef.current}</h1>;
}

ref は、代わりにイベントハンドラまたはエフェクトから読み書きできます。

function MyComponent() {
// ...
useEffect(() => {
// ✅ You can read or write refs in effects
myRef.current = 123;
});
// ...
function handleClick() {
// ✅ You can read or write refs in event handlers
doSomething(myOtherRef.current);
}
// ...
}

レンダー中に何かを読み込みまたは書き込みたい場合は、代わりにstateを使用してください。

これらのルールを破ってもコンポーネントが機能する可能性がありますが、React に追加している新しい機能のほとんどは、これらの期待に依存しています。コンポーネントを純粋に保つことについて詳しくはこちらをご覧ください。


ref を使用した DOM の操作

ref を使用してDOMを操作するのは、特に一般的なことです。React にはこれに対する組み込みのサポートがあります。

まず、初期値が null初期値 を持つref オブジェクトを宣言します

import { useRef } from 'react';

function MyComponent() {
const inputRef = useRef(null);
// ...

次に、操作したい DOM ノードの JSX に、ref オブジェクトを ref 属性として渡します。

// ...
return <input ref={inputRef} />;

React が DOM ノードを作成して画面に配置すると、React は ref オブジェクトの current プロパティをその DOM ノードに設定します。これで、<input> の DOM ノードにアクセスし、focus() などのメソッドを呼び出すことができます。

function handleClick() {
inputRef.current.focus();
}

ノードが画面から削除されると、React は current プロパティを null に戻します。

refs を使用した DOM の操作についてさらに詳しくはこちらをご覧ください。

useRef を使用した DOM 操作の例

1 4:
テキスト入力にフォーカスを当てる

この例では、ボタンをクリックすると入力にフォーカスが当たります。

import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        Focus the input
      </button>
    </>
  );
}


ref の内容の再作成を避ける

React は ref の初期値を一度保存し、次のレンダリングでは無視します。

function Video() {
const playerRef = useRef(new VideoPlayer());
// ...

new VideoPlayer() の結果は最初のレンダリングでのみ使用されますが、この関数はレンダリングごとに呼び出しています。高価なオブジェクトを作成している場合、これは無駄になる可能性があります。

それを解決するには、代わりに ref をこのように初期化するとよいでしょう。

function Video() {
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer();
}
// ...

通常、レンダリング中に ref.current を書き込みまたは読み込むことは許可されていません。ただし、このケースでは、結果が常に同じであり、条件が初期化中にのみ実行されるため、完全に予測可能であるため問題ありません。

詳細解説

後で useRef を初期化するときに null チェックを避ける方法

型チェッカーを使用しており、常に null をチェックしたくない場合は、代わりにこのようなパターンを試すことができます。

function Video() {
const playerRef = useRef(null);

function getPlayer() {
if (playerRef.current !== null) {
return playerRef.current;
}
const player = new VideoPlayer();
playerRef.current = player;
return player;
}

// ...

ここでは、playerRef 自体が nullable です。ただし、getPlayer()null を返すケースはないと型チェッカーに納得させる必要があります。その後、イベントハンドラーで getPlayer() を使用します。


トラブルシューティング

カスタムコンポーネントの ref を取得できない

このように、独自のコンポーネントに ref を渡そうとすると

const inputRef = useRef(null);

return <MyInput ref={inputRef} />;

コンソールにエラーが表示される可能性があります。

コンソール
警告: 関数コンポーネントには ref を指定できません。この ref へのアクセスは失敗します。React.forwardRef() を使用しますか?

デフォルトでは、独自のコンポーネントは内部の DOM ノードへの ref を公開しません。

これを修正するには、ref を取得したいコンポーネントを見つけます。

export default function MyInput({ value, onChange }) {
return (
<input
value={value}
onChange={onChange}
/>
);
}

次に、次のように forwardRef でラップします。

import { forwardRef } from 'react';

const MyInput = forwardRef(({ value, onChange }, ref) => {
return (
<input
value={value}
onChange={onChange}
ref={ref}
/>
);
});

export default MyInput;

これで、親コンポーネントはそれへの ref を取得できます。

別のコンポーネントの DOM ノードへのアクセスについてさらに詳しくはこちらをご覧ください。