useRefを使ってDOMノードを操作している子コンポーネントに親コンポーネントから ref を渡して DOM ノードを取得する

汎用コンポーネントを作っていると、フォーカス制御やキーボード操作のために ref を使っている子コンポーネントに親コンポーネントから ref を渡して DOM ノードを取得したい場面があります。

これは forwardRefuseImperativeHandle を組み合わせて、ref が使われた時に親コンポーネントに渡されるインスタンス値を DOM ノードにすることで実現できます。

import { forwardRef, useImperativeHandle, useRef } from "react";

const Input = forwardRef<HTMLInputElement | null>((_, ref) => {
  const inputRef = useRef<HTMLInputElement>(null);

  useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(
    ref,
    () => inputRef.current
  );

  const onClick = () => {
    inputRef.current?.focus();
  };

  return (
    <div>
      <input type="text" ref={inputRef}></input>
      <button type="button" onClick={onClick}>
        Focus Button
      </button>
    </div>
  );
});

export const App = () => {
  const ref = useRef<HTMLInputElement>(null);

  return <Input ref={ref} />;
};

export default App;