🍣

React + TypeScript | useRef と forwardRef の基礎概念と使いどころ

2023/06/22に公開

対象読者

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()?

エラーキャプチャ

Warning Error

上記、エラーが出現します。これが冒頭で説明した内容です。

親から子供に 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