-
React – 3つのref
- 2018年7月25日
- reactjs
React16.3から、refを操作するための新しいAPIが2つ追加されていました。
・createRef
・forwardRef
ref自体頻繁に使用するものではないと思いますが、
例えばinput要素のfocusを操作したいといった場合にrefを指定することがあります。
今回は上記の2者の中でも利用ケースが多いと想定される以前からのrefと
https://reactjs.org/blog/2018/03/29/react-v-16-3.html#createref-api
React16.3より前のバージョンでは、
https://github.com/facebook/react/pull/8333#issuecomment-271648615
によると、Dan Abramov氏が以下のように述べておられる箇所がありました。
3番目。
その点、
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}} />
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氏が以下のように述べておられる箇所がありました。
1番目の問題としては、
- 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.
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を発火させるパターン。親側で実際の表示内容を定義する。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Parent extends React.Component{ | |
renderRow = (hoge) => { | |
return <div>{hoge}</div>; | |
} | |
render(){ | |
return <DataGrid renderRow={this.renderRow} />; | |
} | |
} | |
class DataGrid extends React.Component{ | |
anyLogic = () => { | |
// any logic here | |
this.setState({ hoge: xxxxxx }); | |
} | |
render(){ | |
return this.props.renderRow(this.state.hoge); | |
} | |
} |
Composable
でない。様々なコンポーネントを組み合わせてアプリケーションを実装していく際に、String Refs
は障害になってしまう。例えば、とあるライブラリがあったとして、ライブラリ内の要素にString Refs
をつけていると、ライブラリの利用側で別のrefをつけることができなくなってしまう。
※記事中のissue#8734 -> https://github.com/facebook/react/issues/8734
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 例:ref`hoge`を呼び出し側で直接指定できない。 | |
class Parent extends React.Component{ | |
someLogic = () => { | |
this.refs.hoge.refs.hoge.focus(); | |
// ↑こんな感じでHogeコンポーネントで定義しているrefを利用側で書かないといけなくなる | |
// もしHogeコンポーネントを仕様変更した場合、呼び出し側も修正しないといけなくなる可能性が高くなる。 | |
} | |
render(){ | |
return <Hoge ref="hoge"/> //←Hogeコンポーネントで指定している`hoge`ではない | |
} | |
} | |
class Hoge extends React.Component{ | |
render(){ | |
return <div ref="hoge"></div> | |
} | |
} |
Callback Refs
であれば柔軟に対応可能。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 例:Hogeのrefを呼び出し側でも参照できるようにする | |
class Parent extends React.Component{ | |
constructor(props){ | |
super(props); | |
this.hogeRef = null; | |
} | |
someLogic = () => { | |
this.hogeRef.focus(); | |
// ↑こんな感じでHogeコンポーネントのrefを利用できる | |
// もしhogeコンポーネントを仕様変更しても影響範囲を狭めることができる。 | |
} | |
render(){ | |
return <Hoge targetRef={(e) => {this.hogeRef = e;}/>; | |
} | |
} | |
class Hoge extends React.Component{ | |
constructor(props){ | |
super(props); | |
this.hogeRef = null; | |
} | |
targetRef = (e) => { | |
this.hogeRef = e; | |
this.props.targetRef(e); // propsのcallbackを使って利用側でもrefが参照できるようにする | |
} | |
anyLogic = () => { | |
this.hogeRef.focus(); // もちろん自分自身もrefを利用可能 | |
} | |
render(){ | |
return <div ref={this.targetRef}></div>; | |
} | |
} |
Creating Refs
Callback Refs
はComposable
なコンポーネント実装を行う際に柔軟に対応できるとのことでした。
一方、16.3でリリースされたCreating Refs
はComposable
を維持しつつも、よりシンプルに実装できます。
Callback Refs
はfunctionとして定義する必要があるのに対し、Creating Refs
は以下のようにrefを定義できます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Callback Refs | |
class CallBackRefs extends React.Component{ | |
constructor(props){ | |
super(props); | |
this.inputRef = null; | |
} | |
anyLogic = () => { | |
this.inputRef.focus(); | |
} | |
render(){ | |
return <input ref={ e => { this.inputRef = e; } } /> // functionとして定義する必要がある | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Creating Refs | |
class CreatingRefs extends React.Component{ | |
constructor(props){ | |
super(props); | |
this.inputRef = React.createRef(); // ①インスタンスを作って、、 | |
} | |
anyLogic = () => { | |
// `current`で要素にアクセスする | |
this.inputRef.current.focus(); | |
} | |
render(){ | |
return <input ref={this.inputRef} /> // ②代入するだけ | |
} | |
} |
まとめ
コンポーネント指向に則り再利用性を考慮しながら柔軟にコンポーネントを組み合わせてアプリを迅速に安全に開発するために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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ForwardRefs extends Component { | |
constructor(props){ | |
super(props); | |
this.childRef = React.createRef(); | |
} | |
handleClick = () => { | |
alert(this.childRef.current.value); | |
} | |
render(){ | |
return ( | |
<div> | |
<Target ref={this.childRef}/> // refを直接指定 | |
<button onClick={this.handleClick}>input value</button> | |
</div> | |
); | |
} | |
} | |
const Target = React.forwardRef((props, ref) => { | |
// `forwardRef`としてコンポーネント定義すると、引数としてrefが渡ってくる | |
// ただし「`stateless component`でのみ使用可能」、という制約がある | |
return ( | |
<div> | |
<p>Target</p> | |
<input ref={ref}/> | |
</div> | |
); | |
}); |
この記事を書いた人 : 國田健史
スタッフブログタグ:
AWS bluebird css CSV docker docker compose electron ES6 es2015 Git Heroku ITコンサルティング JavaScript justinmind less MongoDB Node.js php PostgreSQL Private Space Promise React react-router reactjs Salesforce scss Selenium Builder selenium IDE Selenium WebDriver stylus TypeScript VirtualBox VisualStudioCode vue vuejs webpack システム開発プロジェクト セキュリティ ワイヤーフレーム 上流工程 卒FIT 帳票 要件定義 設計 電力小売業界
一覧へ戻る
AWS bluebird css CSV docker docker compose electron ES6 es2015 Git Heroku ITコンサルティング JavaScript justinmind less MongoDB Node.js php PostgreSQL Private Space Promise React react-router reactjs Salesforce scss Selenium Builder selenium IDE Selenium WebDriver stylus TypeScript VirtualBox VisualStudioCode vue vuejs webpack システム開発プロジェクト セキュリティ ワイヤーフレーム 上流工程 卒FIT 帳票 要件定義 設計 電力小売業界