こんにちは!Reactの学習、楽しく進められていますか?
コンポーネントを組み合わせて画面を作っていく感覚は、まるでレゴブロックのようでワクワクしますよね。
でも、Reactを触り始めて少し経つと、こんな壁にぶつかることがありませんか?
「親コンポーネントから、子コンポーネントの中にある『本当のDOM(inputタグなど)』を直接操作したい!」
「フォーカスを当てたり、高さを測ったりしたいのに、普通にPropsを渡しても上手くいかない……」
そう、Reactの世界では、データは基本的に「親から子へ」一方通行で流れるルールになっています。そのため、子どもの中身(DOM)を親から触ろうとすると、ちょっとしたコツが必要になるんです。
そこで登場するのが、今日お話しする`forwardRef`(フォワード・レフ)です!
名前はちょっといかついですが、大丈夫ですよ。一つひとつ、身近な例えを交えながら優しく紐解いていきましょう。
—
1. `forwardRef`ってなに?(宅配便の「転送サービス」のイメージ)
いきなりコードを書く前に、イメージを掴みましょう。
例えば、あなたが大きな荷物(DOMへの参照 = `ref`)を、マンションの「305号室の佐藤さん」に届けたいとします。
でも、そのマンションの1階には頑丈なオートロックの受付(親コンポーネント)があって、直接305号室のドアまでは行けません。
そこで受付の人に、こう頼みますよね。
「すみません、この荷物、305号室の佐藤さん(子コンポーネントの中にあるinput要素)に転送(forward)してもらえませんか?」
この「受け取った荷物を、奥にある本当の宛先までスルーっとパスする仕組み」が、まさに `forwardRef` です。
通常、ReactのPropsとして `ref` という名前は特別な予約語のような扱いになっていて、そのままでは子コンポーネントにうまく渡せません。「おいおい、そのままじゃ渡せないよ!」とReactが怒ってしまうんです。
そこで `forwardRef` で包んであげることで、「あ、この `ref` は中身のDOMまでパスしていいやつね!」とReactに教えてあげられるわけですね。
—
2. 実際のコードを見てみよう!
百聞は一見にしかず。まずは、ボタンを押したら「カスタム入力欄」に自動でカーソルが当たる(フォーカスされる)という、よくある実用的なコンポーネントを作ってみましょう。
ここでは、今どきのReact(TypeScript)で現場でもバリバリ使われている書き方でご紹介しますね。
import React, { useRef, forwardRef } from ‘react’;
// ① 子コンポーネントのPropsの型定義です
// 今回は特に特別なPropsはないので空っぽですが、将来的にここに独自のPropsを追加できます
type CustomInputProps = {};
// ② forwardRefを使って、親から受け取ったrefを内部のinput要素に「転送」します
//
const CustomInput = forwardRef
return (
{/ 親から受け取った ref を、そのまま生粋の input タグにバトンタッチします /}
);
});
// ③ 親コンポーネント側から使ってみましょう
export default function App() {
// input要素を指し示すための「マジックハンド(ref)」を作ります
const inputRef = useRef
const handleFocusClick = () => {
// マジックハンドがうまくinputを指していれば、フォーカスを当てます!
if (inputRef.current) {
inputRef.current.focus();
inputRef.current.style.borderColor = ‘#007bff’; // ついでに枠線の色も変えちゃおう
}
};
return (
forwardRefのデモ
{/ 子コンポーネントに ref を渡す! /}
);
}
どうでしょう?
親(`App`)で作った `inputRef` が、`CustomInput` という壁をすり抜けて、中の `` タグまでしっかり届いているのが分かりますよね。
—
3. ここでつまずきやすい!TypeScriptの型付けの罠
初心者の頃、筆者もここで何度も頭を抱えたのですが、`forwardRef` をTypeScriptで書くとき、型エラーに悩まされがちです。
ポイントは以下の2つだけ!呪文のように覚えてしまいましょう。
forwardRef
1. 第1ジェネリクス(DOMの型):
「最終的にどのHTML要素を指したいのか」を指定します。
- `input` なら `HTMLInputElement`
- `button` なら `HTMLButtonElement`
- `div` なら `HTMLDivElement`
です。迷ったらエディタの補完を信じましょう!
2. 第2ジェネリクス(Propsの型):
普段コンポーネントを作るときと同じように、自分が定義したPropsの型(InterfaceやType Alias)を渡します。Propsが何もない場合でも、空のオブジェクト型 `{}` を指定してあげるとTypeScriptが機嫌よく動いてくれます。
—
4. チーフアーキテクトからの実務アドバイス
最後に、現場で設計をする上での大切な心構えを一つ。
「DOMを直接操作する(`ref` を使う)」というのは、実はReactの基本理念である「データ駆動型(状態が変われば画面も勝手に変わる)」という世界観から少し外れる、いわば「裏技・特例措置」に近いものです。
なんでもかんでも `forwardRef` や `ref` でDOMを操作しようとすると、コードがスパゲッティのように複雑になってしまい、後から自分で自分の首を絞めることになります。
原則として:
- データの変更や表示の切り替えは、基本的に `useState` や Props などの「状態」で行う。
- どうしても「フォーカスを当てる」「スクロール位置を制御する」「アニメーションやメディア(動画など)を再生する」といった、ブラウザのDOMそのものを動かす必要がある瞬間だけ `forwardRef` を使う。
このメリハリを持つだけで、あなたの書くReactコードは一気にプロのアーキテクトのそれに近づきます。
最初は難しく感じるかもしれませんが、何度か書いていくうちに「あ、ここはパスが必要なやつだな」と直感で分かるようになりますよ。焦らず、ご自身のペースで一歩ずつ進めていきましょう!応援しています!

コメント