【実務・中級編】 forwardRefによるRefの転送 – React実践ガイド

なぜその `ref` は届かないのか? `forwardRef` を極める現場の知見

Reactを触り始めてしばらくすると、必ず直面する壁があります。「親から子へ `ref` を渡したいのに、なぜか `undefined` になる」という現象です。

中級レベルに差し掛かったエンジニアなら、一度は `props` に `inputRef` という名前で渡して凌いだ経験があるはずです。でも、それ、実はアンチパターンです。Reactの哲学からは少し外れた「泥臭い回避策」でしかありません。

今日は、Reactの設計思想の肝である `forwardRef` について、なぜこれが必要なのか、そして現場でどう使うのが一番「美しい」のかを紐解いていきましょう。

—

1. なぜ `ref` は props として受け取れないのか

まず、根本的な話をしましょう。Reactにおいて `ref` は、通常の `props`(`children` や `value` など)とは異なる、「特別な属性」として扱われています。

ブラウザの裏側では、Reactは `ref` を受け取ると、それをコンポーネントのプロパティとして展開するのではなく、React内部の `RefObject` の管理領域に格納します。そのため、関数コンポーネントを普通に定義しても、引数としての `props` には `ref` が含まれません。

これを無理やり `props.myRef` のように渡すのは、コンポーネントのAPI設計として美しくありません。ライブラリ開発者やチームメイトがあなたのコンポーネントを使うとき、「これ、DOMを直接触りたいときはどのpropsに渡せばいいんだっけ?」と迷わせてしまうからです。

そこで登場するのが `forwardRef` です。これは、「このコンポーネントは、外部からの `ref` を受け入れて、内部の特定のDOMに流し込む準備ができていますよ」という公式な意思表示なのです。

—

2. 実践:クリーンな `forwardRef` の実装パターン

現場でよくある、「自作の入力コンポーネントにフォーカスを当てたい」というケースで解説します。ここでのポイントは、「カプセル化を壊さずに、必要な機能だけを外部に露出させる」ことです。

import React, { forwardRef, useImperativeHandle, useRef } from ‘react’;

// 1. forwardRefを使って、親からのrefを受け取れるようにする
// TypeScriptの場合は、ジェネリクスで を指定するのが定石です
const CustomInput = forwardRef(
({ label }, ref) => {
return (


{/ 受け取ったrefを、目的のDOM要素にそのまま渡す /}

);
}
);

// デバッグ用:displayNameを設定しておくと、React DevToolsで追いやすくなる(地味に重要)
CustomInput.displayName = ‘CustomInput’;

export default CustomInput;

親側での使い方

const Parent = () => {
const inputRef = useRef(null);

const handleFocus = () => {
// これだけで、子コンポーネント内のinput要素をダイレクトに操作できる
inputRef.current?.focus();
};

return (
<>



);
};

—

3. さらに一歩先へ:`useImperativeHandle` の魔力

中級者から一歩抜け出すためのTipsをもう一つ。
`forwardRef` で渡すのはDOM要素そのものだけとは限りません。「フォーカスする」以外のメソッド(例えば `clear()` や `validate()` など)を親から叩かせたい場合、DOMを丸ごと露出させるのはカプセル化の観点から危険です。

そんな時こそ `useImperativeHandle` です。

const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef(null);

// 親から見える「インターフェース」を定義する
useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => {
if (inputRef.current) inputRef.current.value = ”;
}
}));

return ;
});

こうすることで、親コンポーネントからは `ref.current.focus()` や `ref.current.clear()` が呼べるようになります。「内部のDOM構造を知らなくても、外部から操作できる機能だけを公開する」。これこそが、保守性の高いコンポーネント設計の極意です。

—

現場のシニアからのアドバイス

最後に、一つだけ。「何でもかんでも `ref` を使わない」という勇気を持ってください。

Reactは「状態(State)」をベースにUIを構築するフレームワークです。DOMを直接操作する `ref` は、Reactの管理下から外れる行為です。多用すればするほど、バグの温床になり、レンダリングの整合性が取れなくなります。

`forwardRef` を使うのは、「フォーカス管理」「スクロール制御」「測定(getBoundingClientRect)」「サードパーティのライブラリ(D3.jsなど)との連携」といった、どうしてもDOMの直接操作が必要な時だけに限定しましょう。

「なぜこれを使っているのか?」を言語化できるエンジニアこそが、最強です。ぜひ、今日からあなたのコードにこの知見を組み込んでみてください。レビュー時の説得力が段違いになるはずですよ。

コメント

タイトルとURLをコピーしました