React16.3から、refを操作するための新しいAPIが2つ追加されていました。
・createRef
・forwardRef
ref自体頻繁に使用するものではないと思いますが、
例えばinput要素のfocusを操作したいといった場合にrefを指定することがあります。
今回は上記の2者の中でも利用ケースが多いと想定されるcreateRef
を取り上げたいと思います。
以前からのrefとcreateRef
https://reactjs.org/blog/2018/03/29/react-v-16-3.html#createref-api
React16.3より前のバージョンでは、
String Refs
string値でrefを指定する
<input ref="hoge" />
Callback Refs
callback関数でコンポーネント要素と任意に宣言した変数とを紐付ける
<input ref={(e) => {this.hoge = e}} />
が利用可能でした。
これらに加えて、React16.3ではさらにcreateRef
が加わりました。
Reactのガイドhttps://reactjs.org/docs/refs-and-the-dom.htmlでは、Creating Refs
という表記があります。
String Refs
はレガシーとされており、いくつか問題があることから、いずれは削除されることが表明されています。
https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
String Refs
にはどんな問題があるか?
https://github.com/facebook/react/pull/8333#issuecomment-271648615
によると、Dan Abramov氏が以下のように述べておられる箇所がありました。
- It requires that React keeps track of currently rendering component (since it can’t guess
this
). This makes React a bit slower.- It doesn’t work as most people would expect with the “render callback” pattern (e.g.
<DataGrid renderRow={this.renderRow} />
) because the ref would get placed onDataGrid
for the above reason.- It is not composable, i.e. if a library puts a ref on the passed child, the user can’t put another ref on it (e.g. #8734). Callback refs are perfectly composable.
1番目の問題としては、String Refs
はthis
の中身を推測することができないがために、表示されているコンポーネントの状態をReact側で保持させておく必要がある。そのため、若干動作が遅くなる、とのことだそうです。
2番目。String Refs
はコンポーネントに直接指定される構造のため、”render callback”パターンのようなことはできないとのこと。
https://github.com/facebook/react/pull/8333#issuecomment-277079817
にあるように、DataGrid
を例にすると、String Refs
はDataGrid
に指定されるため、MyComponent
ではなくDataGrid
を通してアクセスすることになってしまう。
例えばMyComponent
上でrefにアクセスする場合、DataTable
にrefを付与した上で、this.refs.dataGrid.refs.hoge
などどする必要がある。
Callback Refs
だったら直接MyComponent
上で保持し、this.hoge
のようにアクセスできる。
参考:”render callback”パターン
子コンポーネントのrender時にpropsで定義したcallbackを発火させるパターン。親側で実際の表示内容を定義する。
3番目。Composable
でない。様々なコンポーネントを組み合わせてアプリケーションを実装していく際に、String Refs
は障害になってしまう。例えば、とあるライブラリがあったとして、ライブラリ内の要素にString Refs
をつけていると、ライブラリの利用側で別のrefをつけることができなくなってしまう。
※記事中のissue#8734 -> https://github.com/facebook/react/issues/8734
その点、Callback Refs
であれば柔軟に対応可能。
Creating Refs
Callback Refs
はComposable
なコンポーネント実装を行う際に柔軟に対応できるとのことでした。
一方、16.3でリリースされたCreating Refs
はComposable
を維持しつつも、よりシンプルに実装できます。
Callback Refs
はfunctionとして定義する必要があるのに対し、Creating Refs
は以下のようにrefを定義できます。
まとめ
コンポーネント指向に則り再利用性を考慮しながら柔軟にコンポーネントを組み合わせてアプリを迅速に安全に開発するためにCallback Refs
やCreating Refs
の利用が推奨されています。
また、Callback Refs
よりも手軽に利用できるCreating Refs
を使うことでちょっとしたref操作であれば簡単に実現できるようになったのではないでしょうか。
参考:
https://leoasis.github.io/posts/2017/03/27/react-patterns-render-callback/
http://developer.medley.jp/entry/2018/02/27/170000
ーー
続 React16.3
createRef
と一緒に16.3で追加されたAPIとしてforwardRef
があります。
発展途上のもののようですが、参考として調べてみました。
forwardRef
https://reactjs.org/blog/2018/03/29/react-v-16-3.html#forwardref-api
様々なコンポーネントを組み合わせることにより、一つのアプリケーションを作成するわけですが、
通常のrefだと子のコンポーネントで直接指定した場合、そのコンポーネントのrefになってしまいます。
<Child ref={this.childRef}/> // Childのrefになってしまう
なので、子のコンポーネントのさらに子の要素にrefをつけたい場合、わざわざ別の名前でpropsを指定する必要が生じます。
これを解決するのがforwardRef
です。
https://reactjs.org/docs/forwarding-refs.html