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 を使用してください。useRef
と useState
のどちらを選ぶべきかについて詳しくはこちらをご覧ください。
例 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 を使用した 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 の操作についてさらに詳しくはこちらをご覧ください。
例 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
を書き込みまたは読み込むことは許可されていません。ただし、このケースでは、結果が常に同じであり、条件が初期化中にのみ実行されるため、完全に予測可能であるため問題ありません。
詳細解説
型チェッカーを使用しており、常に 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} />;
コンソールにエラーが表示される可能性があります。
デフォルトでは、独自のコンポーネントは内部の 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 ノードへのアクセスについてさらに詳しくはこちらをご覧ください。