Reactのrefについて
refの役割
reactのデータフローはトップダウンで、子コンポーネントの状態を変更する場合のみpropsを渡す必要があることが分かっていますが、特定のシナリオでは、子コンポーネントのインスタンスやdom要素を取得して、その要素の動作を独自に操作したいので、refを使って子コンポーネントやdom要素を取得する必要があるのです
シナリオ: アニメーション、またはある時点で入力ボックスにフォーカスを当てたい
ref.を使うときの3大原則
1. dom要素の上でref属性を使用することができます
2. クラスコンポーネントの上で ref 属性を使用することができます。
3. 関数コンポーネントの上でref属性を使うことはできません。関数コンポーネントはインスタンスを持っておらず、親コンポーネントは子コンポーネントのrefを取得することで、実際には子コンポーネントのインスタンスを取得しているからです(ただし、関数コンポーネントで子コンポーネントのインスタンスのrefを取得して変数に格納すれば、問題ありません)。
個人的には、ファンクションコンポーネントは、ステートやライフサイクルのメソッドが使えない、ref属性が使えないなど、まだまだ制約が多いと感じています(開発中に遭遇した問題、詳細は下記参照)
基本的な使い方
class App extends React.
constructor() {
super();
this.inputRef = React.createRef();
}
handleClick = () => {
this.inputRef.current.focus();
};
render() {
return (
);
}
}
Copy the code
この例では react16.3+バージョン (古いバージョンを使いたい場合は下記を参照)、3つのステップがあります。
1. createRef メソッド refオブジェクトを生成する
2.render は子要素または dom 要素の ref 属性を受け取る
3. this.inputRef.currentを使用して、子要素またはdom要素を取得します。
コールバックはrefを使用します。
一般的にreact16.2以下で使用されます。
componentDidMount() {
if (this.img && this.img.complete) { //this is where you get the img dom element
this.onLoad()
}
}
render() {
const {className, src, alt, onClick, onRef, preview} = this.props
return (
{preview && !src ? (
) : (
{
this.img = node
if (onRef) {
onRef()
}
}}
src={src}
alt={alt}
onLoad={this.onLoad}
onClick={onClick}
/>
)}
)
}
Copy code
ご覧の通り、コールバックを使って直接refを取得することも可能で、個人的にはこちらの方がシンプルだと感じていますが、作者がこの方法を捨てた理由はまだよく分かりません。その コンポーネントをまたいで渡すのが面倒だからだと思うのですが ~. また、コードを見ると、onRefメソッドはrefを直接上位コンポーネントに渡すことができるようなので、新しいバージョンのapiを使ってrefを上位コンポーネントに渡したい場合、その方法はどうすればいいのでしょうか?を参照してください。 forwardRef
フォワードレフ
const MyInput = React.forwardRef((props, ref) => (
<div>
I am Custom input component
<input ref={ref}/>
</div>
))
class App extends React.
constructor() {
super();
this.inputRef = React.createRef();
}
handleClick = () => {
this.inputRef.current.focus();
};
render() {
return (
<div className="App">
<button onClick={this.handleClick}>click me Conduct focus</button>
<MyInput ref={this.inputRef}/>
</div>
);
}
}
Copy the code
上位のコンポーネントの中にあるdom要素や子コンポーネントのインスタンスを取得したい場合は、React.forwardRefを使ってコンポーネントをレイヤーで囲みます。実行順序は 上位コンポーネント createRef => アプリコンポーネントはMyInputにrefを渡す => MyInputはforwardRefメソッドでrefを受け取る => input入力ボックスはrefにノードを渡す(これはすでに上位コンポーネントのrefになっています)。
上位コンポーネントでのrefの使用
forwardRefを使用するシナリオはそれほど多くありませんが、時にはコンポーネントをまたいでrefを渡す必要がある場合があります。あるロジックを実装するために、実際にはコンポーネントをレイヤーにカプセル化した上位レベルのコンポーネントを使用することは知っていますが、上位レベルのコンポーネントでrefを取得したい場合、上位レベルのコンポーネントのrefを取得します、なぜでしょうか?例えば:最上位コンポーネントはApp、上位コンポーネントはMyHOC、子コンポーネントはMyInputと呼ばれ、このMyInputをAppで取得したいので、次のように使うのは間違いです。
次の例では、アプリは実際にはFooのrefを受け取りますが、Fooはrefを持ちません。
// MyInput.js
import MyHOC from '. /MyHOC'
const MyInput = () => (
I am Custom input component
)
return MyHOC(MyInput)
// MyHOC.js
const MyHOC = (WrappedComponent) => {
// do sth here...
class Foo extends React.
render() {
return <WrappedComponent {. .this.props} />;
}
}
return <Foo />;
}
export default MyHOC
//App.js
import MyInput from '. /MyInput';
const ref = React.createRef();
<MyInput
ref={ref} // The ref here is actually Foo
/>;
Copy the code
では、Hocではどのように使うのが正しいのでしょうか?
// MyInput.js
import MyHOC from '. /MyHOC'
const MyInput = () => (
I am Custom input component
)
return MyHOC(MyInput)
// MyHOC
const MyHOC = (WrappedComponent) => {
class Foo extends React.
render() {
const { forwardedRef, . .rest } = this.props;
// At this point the ref is the WrappedComponent
return <WrappedComponent ref={forwardedRef} {. .rest} />;
}
}
// Wrap a layer with React.forwardRef, so that the ref of the higher-level component is named forwardedRef (an arbitrarily defined property name) and passed to the child component
return React.forwardRef((props, ref) => {
return <Foo {. .props} forwardedRef={ref} />;
});
}
export default MyHOC
//App.js
import MyInput from '. /MyInput';
const ref = React.createRef();
<MyInput
ref = {ref}
/>;
Copy code
上のコードにあるように、違いはただ一つ。
MyHOC
コンポーネントは
React.forwardRef
を渡しているため、外側のコンポーネントが受け取る参照はFooではなく、本物のMyInputコンポーネントとなります。
問題点と解決策
- 機能コンポーネントがref属性を使えない(開発中もこの問題に遭遇しました、ant designのgetFieldDecorator()(myFnComponent)を使うと、"Unable to mount ref's" というエラーが報告されました)
<Form.Item
label={field.label}
key={
`key_${field.label}`}
>
{render
? render()
: getFieldDecorator(field.fieldName, {
rules: R.pathOr([], ['config', 'rules'], field),
initialValue: data[field.field],
})(
- )} // The Item here is a functional component, so this will be wrong
// })(Item(field))} This is ok, because Item will return a class component
// Error message: Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.
Copy the code
- 1つのコンポーネントの中に複数のrefがある場合はどうでしょうか?その場合は、refを複数回作成します。
-
注意:コンポーネントがマウントされる前にthis.myRef.currentを取得した場合、そのコンポーネントは
null
で返され、refはcomponentDidMount
またはcomponentDidUpdate
を更新する前に
参考リンク
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
ハートビート・エフェクトのためのHTML+CSS
-
HTML ホテル フォームによるフィルタリング
-
HTML+cssのボックスモデル例(円、半円など)「border-radius」使いやすい
-
HTMLテーブルのテーブル分割とマージ(colspan, rowspan)
-
ランダム・ネームドロッパーを実装するためのhtmlサンプルコード
-
Html階層型ボックスシャドウ効果サンプルコード
-
QQの一時的なダイアログボックスをポップアップし、友人を追加せずにオンラインで話す効果を達成する方法
-
sublime / vscodeショートカットHTMLコード生成の実装
-
HTMLページを縮小した後にスクロールバーを表示するサンプルコード
-
html のリストボックス、テキストフィールド、ファイルフィールドのコード例