React + TypeScript | useRef と forwardRef の基礎概念と使いどころ
対象読者
Reactの仕様上、親の Function コンポーネントから 子の Function コンポーネント に ref を props で渡して、props 経由で受け取った ref を子供で参照することができないようになっているから、親の Function コンポーネントから 子の Function コンポーネント に ref を渡したい場合は、子供の Function コンポーネントを forwardRef
でラッピングしないといけない
↑ の説明を聞いたときに「???」となった方は読み進めてください
↑ の説明を聞いたときに「完全理解」となった方は、ここで早期returnしてください
解説
では、冒頭で述べた文章に関してコードベースで解説を進めます。
まずは、以下のようなコンポーネントを作成します。
子供のコンポーネント①
import { FC } from 'react';
export const Children: FC = () => {
return <div>Children</div>
}
親コンポーネント①
↑で作成したコンポーネントを親で呼び出します
import { FC } from 'react';
export const Parent: FC = () => {
return <Children />
};
さて、理由はさておき、親から子供にrefを渡したいという状況になりました。
親から子供にrefを渡すようにコードを修正してみましょう。
子供のコンポーネント②
import type { FC, RefObject } from 'react';
type Props = { ref: RefObject<HTMLDivElement> }
export const Children: FC<Props> = ({ ref }) => {
return <div ref={ref}>Children</div>
}
親コンポーネント②
import { FC, useRef } from 'react';
export const Parent: FC = () => {
const divRef = useRef<HTMLDivElement>(null);
return <Children ref={divRef} />
};
さて、このコードで開発環境を起動してブラウザで確認をしてみましょう。
結果はどうなると思いますか?
DevTools の Console タブを開いてみてください
エラー文
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
エラーキャプチャ
上記、エラーが出現します。これが冒頭で説明した内容です。
親から子供に ref を渡したい場合は、子供側で forwardRef で コンポーネント をラッピングする必要があるわけです。今回は、子供側で forwardRef で コンポーネント をラッピングしていないので、React先生に「使い方が違うよ」と怒られてしまったわけです。
なので、怒られないように修正する必要があります。
子供のコンポーネント③
import { forwardRef } from 'react';
import type { FC, Ref, RefObject } from 'react';
type Props = Record<string, unknown>
const Children = (props: Props, ref: Ref<HTMLDivElement>) => {
return <div ref={ref}>Children</div>
}
// MEMO : Generics の第一引数に指定するのは ref の型, 第二引数に指定するのは Props の型
export const ChildrenWrapper = forwardRef<HTMLDivElement, Props>(Children);
親コンポーネント③
import { FC, useRef } from 'react';
export const Parent: FC = () => {
const divRef = useRef<HTMLDivElement>(null);
return <ChildrenWrapper ref={divRef} />
};
さて、このコードで開発環境を起動してブラウザで確認をしてみましょう。
結果はどうなると思いますか?
Warning Error が消えているはずです。
改めて、結論をまとめます。
まとめ
Reactの仕様上、親の Function コンポーネントから 子の Function コンポーネント に ref を props で渡して、props 経由で受け取った ref を子供で参照することができないようになっているから、親の Function コンポーネントから 子の Function コンポーネント に ref を渡したい場合は、子供の Function コンポーネントを forwardRef
でラッピングしないといけない
Discussion