Closed6
Reactのrefをpropsとして渡す
扱うこと
- useRefとは
- Refの使い道(4つ)
- なぜ囲わないと親コンポーネントからRefを渡すことができないのか
- 使用方法
- 注意点
- 使用できるメソッドを限定したい場合
- 落とし穴
公式ドキュメントに書いてある通り、forwardRef
で囲ってあげれば良い感じにできる
概要
- forwardRef は、親コンポーネントに対して DOM ノードを ref として公開できるようにします。
- forwardRef() を呼び出すことで、コンポーネントが ref を受け取ってそれを子コンポーネントに転送 (forward) できるようになります。
- 引数:コンポーネントのレンダー関数を受け取る。
- 返り値:JSXでレンダーできるReactコンポーネントを返す
使用方法
- コンポーネント定義を
forwardRef()
でラップする- 親コンポーネントに DOM ノードを公開する
- デフォルトでは、各コンポーネント内の DOM ノードはプライベート
- 時には親にDOMノードを公開したい時がある
- たとえばノードにフォーカスを与えることを許可したい場合
注意点
- コンポーネント内の DOM ノードへの ref を公開することで、後でコンポーネントの内部を変更するのが難しくなることに注意してください。
- 通常は、ボタンやテキスト入力フィールドなどの再利用可能な低レベルコンポーネントからは DOM ノードの公開を行いますが、アバターやコメントのようなアプリケーションレベルのコンポーネントでは行いません。
使用できるメソッドを限定したい場合
- useImperativeHandleを使用することで限定することができる
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} />;
});
落とし穴
- ref は、props として表現できない、命令型の動作にのみ使用するべきです。
- 例えば、ノードへのスクロール、ノードへのフォーカス、アニメーションのトリガ、テキストの選択などです。
- 何かを props として表現できる場合は、ref を使用すべきではありません。
- 例えば、Modal コンポーネントから { open, close } のような命令型のハンドルを公開するのではなく、<Modal isOpen={isOpen} /> のように、isOpen を props として受け取る方が良いでしょう。命令型の動作を props として公開する際にはエフェクトが役立ちます。
refを渡すには囲う必要がある理由
関数コンポーネントにはインスタンスがないため、デフォルトでは関数コンポーネントに ref 属性を使用することはできません。
https://ja.legacy.reactjs.org/docs/refs-and-the-dom.html#refs-and-function-components
before
type ButtonProps = {
onClick: () => void;
ref: (instance: HTMLButtonElement | null) => void;
};
const ShowAllButton = ({
ref,
onClick: handleClick
}: ShowAllButtonProps) => {
return (
<Box>
<Button
ref={ref}
endIcon={<RightIcon sx={{ color: 'primary.main' }} />}
onClick={handleClick}
>
<Typography variant="button">表示</Typography>
</Button>
</Box>
);
};
refをpropsの一部として渡そうしたが、そのままでは渡すことができずundefinedになる
after
type ButtonProps = {
onClick: () => void;
};
const ShowAllButton = forwardRef(function ShowAllButton(
{ onClick: handleClick }: ButtonProps,
ref: Ref<HTMLButtonElement>
) {
return (
<Box>
<Button
ref={ref}
endIcon={
<RightIcon sx={{ color: 'primary.main' }} />
}
onClick={handleClick}
>
<Typography variant="button">表示</Typography>
</Button>
</Box>
);
});
forwardRefで囲うことでrefを受け取れるようになる。
useRef
useRef
は、レンダー時には不要な値を参照するための React フック- →Ref は、出力されるコンポーネントの外見に影響しないデータを保存するのに適している
- →レンダーを跨いで情報を保存できる(通常の変数は、レンダーごとにリセットされる)
- ref はただの JavaScript オブジェクトですので、変更されたとしても、それを React が知ることはできない
Refの使用場面
- 出力されるコンポーネントの外見に影響しないデータを保存する時
- input要素をフォーカス
- スクロールして画像を表示
- 動画の再生・停止
通常の値と何が異なる?
- ref.current プロパティは書き換えが可能です。つまり state と違いミュータブル (mutable)
- ref.current プロパティを変更しても、React はコンポーネントを再レンダーしない
なぜ囲わないとダメなのか?
-
forwardRefを使用しないと、親コンポーネントが渡したrefは子コンポーネント全体のインスタンスを指す
-
だが、子コンポーネントの特定のDOM要素にアクセスするには、そのrefが正しいDOM要素を指す必要がある
- forwardRefは、このrefを適切なDOM要素に「転送(フォワード)」する役割を果たす
-
forwardRefを使うことで、親コンポーネントから渡されたrefが子コンポーネント内の特定のDOM要素を指せる
- これにより、親コンポーネントが子コンポーネントの内部のDOM要素に直接アクセスし、操作することができるようになる
- forwardRefを使わない場合、親コンポーネントは子コンポーネント全体に対する参照しか持つことができず、内部の特定のDOM要素にアクセスすることができない。
一緒に複数のrefを渡すには?
このスクラップは10日前にクローズされました