フロントエンド

forwardRefをざっくり理解

tanesan

forwardRefを使う機会があったのでまとめてみました。

はじめに

まずはfowardRefについて概要を確認してみましょう。公式ページに以下の記述があります。

forwardRef lets your component expose a DOM node to parent component with a ref.

訳すと、「forwardRef は、コンポーネントが親コンポーネントに DOM ノードを参照として公開することを可能にします。」
となります。と言われてもイマイチわからないと思うので、例を交えつつfowardRefの紹介をしていきます。

forwardRefを使ってみる

早速forwardRefを使ったコンポーネントで動作確認と行きたいところですが、順を追って理解するために、まずはuseRefのみを使ったコンポーネントで動作確認していきます。

import React, { useRef } from "react";

export const MyInput = () => {
  const ref = useRef();
  const handleClick = () => {
    ref.current.focus();
  };
  return (
    <>
      <input ref={ref} />
      <button onClick={handleClick}>focus</button>
    </>
  );
};
import { MyInput } from "./MyInput";
import "./styles.css";

export default function App() {
  return (
    <div className="App">
      <MyInput />
    </div>
  );
}

MyInput.js内で、組み込みコンポーネントinputのref属性に、useRefで生成したrefオブジェクトを渡しています。これにより、refオブジェクトのcurrentからDOMノードのAPIを使用することができます。ここでは、ボタンクリック時にfocusメソッドを使用しています。

上記の例では、refオブジェクトの生成とinputへのrefの受け渡しをMyInputコンポーネントで行なっています。では、refの生成をAppで行い、propとしてMyInputに渡すとどうなるのでしょうか。プログラムを下記の通り書き換えます。

import React from "react";

export const MyInput = ({ ref }) => {
  const handleClick = () => {
    ref.current.focus();
  };
  return (
    <>
      <input ref={ref} />
      <button onClick={handleClick}>focus</button>
    </>
  );
};
import { useRef } from "react";
import { MyInput } from "./MyInput";
import "./styles.css";

export default function App() {
  const ref = useRef();
  return (
    <div className="App">
      <MyInput ref={ref} />
    </div>
  );
}

一見動きそうですが、コンソールを見ると下記の警告が出ています。実際にfocusボタンを押してもエラーになってしまいます。(自分も最初このように書いてコンソールに怒られました)

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

警告を見ていきましょう。訳すと、「関数コンポーネントにrefを与えることはできません。この参照にアクセスしようとすると、失敗します。React.forwardRef()を使おうとしたのでしょうか?」となります。また、useRefのトラブルシュートを見てみましょう。その中に下記のような記述があります。

By default, your own components don’t expose refs to the DOM nodes inside them.

以上をまとめると、自作のコンポーネント内のDOMノード(今回の場合はinput)のrefを親コンポーネントに公開することはできないようです。では、どうすればいいのかというと、警告文にもありますがfowardRefを使います。

forwardRefを使用してプログラムを書き換えます。

import React, { forwardRef } from "react";

export const MyInput = forwardRef(({ ref }) => {
  const handleClick = () => {
    ref.current.focus();
  };
  return (
    <>
      <input ref={ref} />
      <button onClick={handleClick}>focus</button>
    </>
  );
});

※App.jsxは変更なし

上記のようにfowardRefの引数に自作のコンポーネント渡すことで、自作のコンポーネント内のrefを公開することができました。実際にボタンを押すと最初の例と同様にinputにフォーカスが当たリます。ここで改めて、冒頭のforwardRefの概要を確認していただくと、説明に納得感があると思います。

まとめ

今回は簡単にですが、fowardRefの紹介をさせていただきました。この記事を通して、少しでも理解の助けになれば幸いです。
また、より詳しい解説・使用例は参考にある本家のサイトで確認いただければと思います。

参考

https://beta.reactjs.org/reference/react/forwardRef
https://beta.reactjs.org/reference/react/useRef

AUTHOR
tanesan
tanesan
記事URLをコピーしました