概要
Refは、DOMノードやタイムアウトIDなど、レンダリングに使用されない情報をコンポーネントに保持させることができる。
Stateとは異なり、Refを更新してもコンポーネントは再レンダリングされません。RefはReactのパラダイムからの「逃げ道」です。組み込みのブラウザAPIなど、React以外のシステムで動作させる必要がある場合に便利です。
useRef
useRefは同じオブジェクトを返します。currentプロパティを変更して情報を保存して、あとでそれを読み出すことができます。
refを変更しても、再レンダリングは行われません。 つまり、refはコンポーネントの視覚的な出力に影響を与えない情報を保存するのに最適なのです。例えば、インターバルIDを保存しておき、後でそれを取得する必要がある場合、それをrefに入れることができます。ref内の値を更新するには、そのcurrentプロパティを手動で変更する必要があります。
refを使用することによるメリット
- 再レンダリングされても情報を保存することができる
- refを変更しても、再レンダリングのトリガーにはならない(stateとは異なります。)
- コンポーネント間で共有される。
refを変更しても再レンダリングを行わないということは、画面に表示したい情報を保存するにはrefは適切ではありません。
上のcodesandboxの中で、{ref.current}
を画面に表示するように修正しても、ボタンをクリックしてref.current
に加算してるのにも関わらず画面は再レンダリングされないために表示は0
の初期値のまま変わりません。
refを利用してDOMを操作する
refオブジェクトを宣言します。useRef
の引数には初期値を入れます。
import { useRef } from 'react';
function MyComponent() {
const inputRef = useRef(null);
// ...
次に、作成したrefオブジェクトを操作したいDOMのref属性に代入します。
return <input ref={inputRef} />;
refオブジェクトのcurrentプロパティからのDOMノードにアクセスして、forcus()
などのDOMのメソッドを呼び出すことができます。
function handleClick() {
inputRef.current.focus();
}
useRefを使用した例
カスタムコンポーネントに対して、refを使用したい時
const inputRef = useRef(null);
return <MyInput ref={inputRef} />;
↑ だとカスタムコンポーネントにrefsを与えられない旨のWarningが表示されます。
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
そんな時は forwardRef
でラップして、その中のDOMにref属性を与えることで親コンポーネントからrefを取得することができるようになります。
import { forwardRef } from 'react';
const MyInput = forwardRef(({ value, onChange }, ref) => {
return (
<input
value={value}
onChange={onChange}
ref={ref}
/>
);
});
export default MyInput;
useImperativeHandle
refで参照できるDOMの関数をカスタマイズすることができます。
例えば、 DOMノード全体を公開するのではなく、その中の2つのメソッド(focusとscrollIntoView)のみを使用したいとします。これを行うには、実際のブラウザDOMを別のrefに保持します。そして、useImperativeHandle を使って親コンポーネントが呼び出したいメソッドのforcus()
とscrollIntoView()
だけを含む関数を定義します。
import { forwardRef, useRef, useImperativeHandle } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);
return <input {...props} ref={inputRef} />;
});
useImprerativeHandleの使用例
カスタマイズ
使用するメソッドは、DOM のメソッドと完全に一致する必要はありません。なので、開発者が任意にカスタマイズして処理を作成することも可能です。
以下の例では、ボタンをクリックした際に親コンポーネントがリストをスクロールしてテキストボックスにフォーカスを当てる動きを scrollAndFocusAddComment
が行っています。
参考