非推奨

React 19では、forwardRef は不要になりました。代わりに、ref をpropsとして渡してください。

forwardRef は将来のリリースで廃止される予定です。詳細はこちらをご覧ください

forwardRef を使用すると、コンポーネントは ref を使用して親コンポーネントにDOMノードを公開できます。

const SomeComponent = forwardRef(render)

リファレンス

forwardRef(render)

コンポーネントがrefを受信し、それを子コンポーネントに転送できるようにするには、forwardRef() を呼び出します

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
// ...
});

以下の例をご覧ください。

パラメータ

  • render: コンポーネントのレンダリング関数。Reactはこの関数を、コンポーネントが親から受信したpropsとref を使用して呼び出します。返されるJSXは、コンポーネントの出力になります。

戻り値

forwardRef は、JSXでレンダリングできるReactコンポーネントを返します。単純な関数として定義されたReactコンポーネントとは異なり、forwardRef によって返されるコンポーネントは、ref propを受信することもできます。

注意事項

  • StrictModeでは、Reactはレンダー関数を2回呼び出し意図しない副作用を見つけるのに役立てます。これは開発モードのみの動作であり、本番環境には影響しません。レンダー関数が純粋関数である場合(そうあるべきです)、これはコンポーネントのロジックに影響を与えません。いずれかの呼び出しの結果は無視されます。

render 関数

forwardRef は引数としてレンダー関数を受け取ります。Reactはこの関数をpropsref を使って呼び出します。

const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
});

パラメータ ...

  • props: 親コンポーネントから渡される props です。

  • ref: 親コンポーネントから渡される ref 属性です。ref はオブジェクトまたは関数です。親コンポーネントが ref を渡していない場合、null になります。受け取った ref を別のコンポーネントに渡すか、useImperativeHandle に渡す必要があります。

戻り値 ...

forwardRef は、JSX でレンダリングできる React コンポーネントを返します。単純な関数として定義された React コンポーネントとは異なり、forwardRef によって返されるコンポーネントは ref prop を受け取ることができます。


使用方法 ...

親コンポーネントへの DOM ノードの公開 ...

デフォルトでは、各コンポーネントの DOM ノードはプライベートです。ただし、フォーカスを当てるなど、親に DOM ノードを公開すると便利な場合があります。オプトインするには、コンポーネント定義を forwardRef() でラップします。

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} />
</label>
);
});

props の後に2番目の引数として ref を受け取ります。公開する DOM ノードに渡します。

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});

これにより、親の Form コンポーネントは、MyInput によって公開された <input> DOM ノード にアクセスできます。

function Form() {
const ref = useRef(null);

function handleClick() {
ref.current.focus();
}

return (
<form>
<MyInput label="Enter your name:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}

この Form コンポーネントは ref を渡します MyInput へ。MyInput コンポーネントは、その ref を <input> ブラウザタグに *転送* します。その結果、Form コンポーネントはその <input> DOM ノードにアクセスし、focus() を呼び出すことができます。

コンポーネント内の DOM ノードへの ref を公開すると、後でコンポーネントの内部を変更することが難しくなることに注意してください。通常、ボタンやテキスト入力などの再利用可能な低レベルコンポーネントから DOM ノードを公開しますが、アバターやコメントなどのアプリケーションレベルコンポーネントでは行いません。

ref 転送の例 ...

1... 2:
テキスト入力へのフォーカス

ボタンをクリックすると、入力にフォーカスが当たります。`Form`コンポーネントはrefを定義し、それを`MyInput`コンポーネントに渡します。`MyInput`コンポーネントはそのrefをブラウザの`<input>`に転送します。これにより、`Form`コンポーネントは`<input>`にフォーカスを当てることができます。

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}


複数のコンポーネントを介したrefの転送 ...

DOMノードに`ref`を転送する代わりに、`MyInput`のような独自のコンポーネントに転送できます。

const FormField = forwardRef(function FormField(props, ref) {
// ...
return (
<>
<MyInput ref={ref} />
...
</>
);
});

`MyInput`コンポーネントがその`<input>`にrefを転送する場合、`FormField`へのrefはその`<input>`を提供します。

function Form() {
const ref = useRef(null);

function handleClick() {
ref.current.focus();
}

return (
<form>
<FormField label="Enter your name:" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}

`Form`コンポーネントはrefを定義し、`FormField`に渡します。`FormField`コンポーネントはそのrefを`MyInput`に転送し、`MyInput`はそれをブラウザの`<input>` DOMノードに転送します。このようにして、`Form`はそのDOMノードにアクセスします。

import { useRef } from 'react';
import FormField from './FormField.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <FormField label="Enter your name:" ref={ref} isRequired={true} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}


DOMノードの代わりに命令型ハンドルを公開する ...

DOMノード全体を公開する代わりに、より制限されたメソッドセットを持つ、*命令型ハンドル*と呼ばれるカスタムオブジェクトを公開できます。これを行うには、DOMノードを保持するために別のrefを定義する必要があります。

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

// ...

return <input {...props} ref={inputRef} />;
});

受け取った`ref`を`useImperativeHandle`に渡し、`ref`に公開したい値を指定します。

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

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);

return <input {...props} ref={inputRef} />;
});

一部のコンポーネントが`MyInput`へのrefを取得する場合、DOMノードではなく、`{ focus, scrollIntoView }`オブジェクトのみを受け取ります。これにより、DOMノードに関する公開情報を最小限に抑えることができます。

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
    // This won't work because the DOM node isn't exposed:
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput placeholder="Enter your name" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

命令型ハンドルの使用方法の詳細を読む。

落とし穴

**refを過剰に使用しないでください。** refは、propsとして表現できない*命令型*の動作(ノードへのスクロール、ノードへのフォーカス、アニメーションのトリガー、テキストの選択など)にのみ使用する必要があります。

**propsとして表現できる場合は、refを使用しないでください。** たとえば、`Modal`コンポーネントから`{ open, close }`のような命令型ハンドルを公開する代わりに、`<Modal isOpen={isOpen} />`のように`isOpen`をpropsとして受け取る方が良いです。**エフェクト**は、propsを介して命令型の動作を公開するのに役立ちます。


トラブルシューティング ...

コンポーネントは`forwardRef`でラップされていますが、それへの`ref`は常に`null`です ...

これは通常、受け取った`ref`を実際に使用することを忘れたことを意味します。

たとえば、このコンポーネントはその`ref`で何もしていません。

const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input />
</label>
);
});

修正するには、ref を DOM ノードまたは ref を受け入れることができる別のコンポーネントに渡します。

const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
});

ロジックの一部が条件付きの場合、MyInput への refnull になる可能性もあります。

const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
{showInput && <input ref={ref} />}
</label>
);
});

showInputfalse の場合、ref はどのノードにも転送されず、MyInput への ref は空のままになります。この例のように、条件が Panel のような別のコンポーネント内に隠されている場合、これは特に見落としがちです。

const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
<Panel isExpanded={showInput}>
<input ref={ref} />
</Panel>
</label>
);
});