Movatterモバイル変換


[0]ホーム

URL:


We want to hear from you!Take our 2021 Community Survey!
このサイトの更新は終了しました。ja.react.dev へ

ref のフォワーディング

この記事は古くなっており、今後更新されません。新しい React 日本語ドキュメントであるja.react.dev をご利用ください。

以下の新しいドキュメントで最新の React の使い方がライブサンプル付きで学べます。

ref のフォワーディングはあるコンポーネントを通じてその子コンポーネントのひとつにref を自動的に渡すテクニックです。これは基本的にはアプリケーション内のほとんどのコンポーネントで必要ありません。しかし、コンポーネントの種類によっては、特に再利用可能なコンポーネントライブラリにおいては、便利なものとなるかもしれません。一般的なシナリオについて以下で述べます。

DOM コンポーネントに ref をフォワーディングする

ネイティブのbutton DOM 要素をレンダーするFancyButton というコンポーネントを考えてみましょう:

functionFancyButton(props){return(<buttonclassName="FancyButton">{props.children}</button>);}

React コンポーネントは、レンダーの結果も含め、実装の詳細を隠蔽します。FancyButton を使用する他のコンポーネントは内側のbutton DOM 要素に対するref を取得する必要は通常ありません 。これは、互いのコンポーネントの DOM 構造に過剰に依存することを防ぐので、良いことです。

そういったカプセル化はFeedStoryComment のようなアプリケーションレベルのコンポーネントでは望ましいことではありますが、FancyButtonMyTextInput といった非常に多くのところで再利用可能な “末梢の” コンポーネントでは不便である可能性があります。このようなコンポーネントは、アプリケーションのいたるところで通常の DOM であるbuttoninput と同様に扱われる傾向にあり、フォーカス、要素の選択、アニメーションをこなすにはそれら DOM ノードにアクセスすることが避けられないかもしれません。

ref のフォワーディングはオプトインの機能であり、それにより、コンポーネントがref を受け取って、それをさらに下層の子に渡せる(つまり、ref を “転送” できる)ようになります。

下の例では、FancyButton は渡されたref を取得して、それをレンダーするbutton DOM にフォワーディングするために、React.forwardRef を使っています。

const FancyButton= React.forwardRef((props, ref)=>(<buttonref={ref}className="FancyButton">{props.children}</button>));// You can now get a ref directly to the DOM button:const ref= React.createRef();<FancyButtonref={ref}>Click me!</FancyButton>;

このように、FancyButton を使ったコンポーネントは下層のbutton DOM ノードの ref を取得することができ、必要であればbutton DOM を直接使うかのように、DOM にアクセスすることができます。

上の例で、何が起こっているかを順々に説明します。

  1. React.createRef を呼び、React ref をつくり、それをref 変数に代入します。
  2. ref<FancyButton ref={ref}> に JSX の属性として指定することで渡します。
  3. React はref を、forwardRef 内の関数(props, ref) => ... の 2 番目の引数として渡します。
  4. この引数として受け取ったref<button ref={ref}> に JSX の属性として指定することで渡します。
  5. この ref が紐付けられると、ref.current<button> DOM ノードのことを指すようになります。

補足

2 番目の引数refReact.forwardRef の呼び出しを使ってコンポーネントを定義したときにだけ存在します。通常の関数またはクラスコンポーネントはref 引数を受け取らず、ref は props からも利用できません。

ref のフォワーディング先は DOM コンポーネントだけにとどまりません。クラスコンポーネントインスタンスに対しても ref をフォワーディングできます。

コンポーネントライブラリのメンテナ向けの補足

コンポーネントライブラリの中で、forwardRef を使い始めた場合、破壊的変更として扱い、新しいメジャーバージョンをリリースすべきです。ライブラリが外から見て今までと違う挙動(例えば、どの値が ref に代入されるかや、どの型がエクスポートされるのか)をする可能性があり、古い挙動に依存しているアプリケーションや他のライブラリを壊す可能性があるからです。

React.forwardRef が存在する場合だけ、条件的にReact.forwardRef を適用することも同じ理由で推奨されません:そのような実装は、React そのものを更新したとき、ライブラリがどのように振る舞うかを変えてしまい、ユーザのアプリケーションを破壊する可能性があるからです。

高階コンポーネントにおける ref のフォワーディング

このテクニックは高階コンポーネント(HOC としても知られています)においても特に便利です。コンポーネントの props をコンソールにログ出力する HOC を例として考えてみましょう。

functionlogProps(WrappedComponent){classLogPropsextendsReact.Component{componentDidUpdate(prevProps){      console.log('old props:', prevProps);      console.log('new props:',this.props);}render(){return<WrappedComponent{...this.props}/>;}}return LogProps;}

“logProps” HOC はすべてのprops をラップするコンポーネントに渡すので、レンダーされる出力は同じになるでしょう。例えば、“fancy button” コンポーネントに渡されるすべての props をログとして記録するために、この HOC を使用することができます。

classFancyButtonextendsReact.Component{focus(){// ...}// ...}// Rather than exporting FancyButton, we export LogProps.// It will render a FancyButton though.exportdefaultlogProps(FancyButton);

ところが上記の例には欠陥があります。これでは ref が渡されないのです。ref は props のひとつではないからです。key と同様に ref は React では props とは違う扱いになります。HOC に対する ref を追加した場合、ラップされたコンポーネントではなく、一番外側のコンテナコンポーネントを参照します。

これはFancyButton コンポーネントに紐付けられることを意図した ref が、実際にはLogProps コンポーネントに紐付けられてしまうことを意味します。

import FancyButtonfrom'./FancyButton';const ref= React.createRef();// The FancyButton component we imported is the LogProps HOC.// Even though the rendered output will be the same,// Our ref will point to LogProps instead of the inner FancyButton component!// This means we can't call e.g. ref.current.focus()<FancyButtonlabel="Click Me"handleClick={handleClick}ref={ref}/>;

幸いにも、React.forwardRef API を使って、内側のFancyButton コンポーネントに対して ref を明示的に転送することができます。React.forwardRef は render 関数を受け取り、その関数はpropsref を引数として取り、React ノードを返します。例えば、

functionlogProps(Component){classLogPropsextendsReact.Component{componentDidUpdate(prevProps){      console.log('old props:', prevProps);      console.log('new props:',this.props);}render(){const{forwardedRef,...rest}=this.props;// Assign the custom prop "forwardedRef" as a refreturn<Componentref={forwardedRef}{...rest}/>;}}// Note the second param "ref" provided by React.forwardRef.// We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"// And it can then be attached to the Component.return React.forwardRef((props, ref)=>{return<LogProps{...props}forwardedRef={ref}/>;});}

DevTools でのカスタム名表示

React.forwardRef は render 関数を受け取ります。React DevTools は ref をフォワーディングしているコンポーネントとして何を表示すべきかを決定するために、この関数を使います。

例えば、次のコンポーネントは ”ForwardRef” として DevTools に表示されます。

const WrappedComponent= React.forwardRef((props, ref)=>{return<LogProps{...props}forwardedRef={ref}/>;});

render 関数に名前をつけると、DevTools はその名前を含めるようになります(例: ”ForwardRef(myFunction)”):

const WrappedComponent= React.forwardRef(functionmyFunction(props, ref){return<LogProps{...props}forwardedRef={ref}/>;});

ラップしているコンポーネントを含めるために、render 関数のdisplayName を設定することもできます:

functionlogProps(Component){classLogPropsextendsReact.Component{// ...}functionforwardRef(props, ref){return<LogProps{...props}forwardedRef={ref}/>;}// Give this component a more helpful display name in DevTools.// e.g. "ForwardRef(logProps(MyComponent))"const name= Component.displayName|| Component.name;  forwardRef.displayName=`logProps(${name})`;return React.forwardRef(forwardRef);}

Is this page useful?このページを編集する

[8]ページ先頭

©2009-2025 Movatter.jp