Refs提供了一個(gè)訪問render()方法內(nèi)DOM節(jié)點(diǎn)或者ReactNode的方法
典型的React數(shù)據(jù)流中誓沸,props是父組件和子組件交互的唯一手段盆偿。要修改一個(gè)子組件撕阎,就需要使用新的props重新渲染它。然而斗这,確實(shí)存在少數(shù)一些情況需要命令性地(imperatively
)修改一個(gè)子節(jié)點(diǎn)而不是通過典型的props數(shù)據(jù)流方式动猬。被修改的子節(jié)點(diǎn)可能是一個(gè)React組件實(shí)例(比如調(diào)用某個(gè)子組件的實(shí)例方法),亦或是一個(gè)DOM元素(比如手動(dòng)地控制某個(gè)input標(biāo)簽聚焦)
表箭。對(duì)于這兩種情況赁咙,React都提供了各種處理方法,refs就是其中的一種燃逻。
1. refs適用的場景
- 手動(dòng)控制DOM節(jié)點(diǎn)
- 獲取子組件的尺寸或者實(shí)例方法
- 和第三方DOM庫集成(典型的jPlayer)
注意:不要濫用refs序目,比如:在使用antd的<Modal />時(shí),可以直接通過修改props.visible為true或false即可實(shí)現(xiàn)Modal組件的顯示和隱藏伯襟,則大可不必使用該組件的show()、hide()方法
2. 創(chuàng)建Refs
注意:Ract 16.3引入的API React.createRef()握童。比較舊的React版本姆怪,保留了refs關(guān)鍵字。不管是新舊版本澡绩,都建議使用refs回調(diào)方式(最后的有對(duì)應(yīng)的示例)稽揭。本文主要實(shí)現(xiàn)一個(gè)
頁面加載時(shí),Input組件自動(dòng)聚焦肥卡,并且在點(diǎn)擊Button組件時(shí)聚焦Input組件
的功能溪掀,使用的方法為React.createRef()。github代碼庫中有對(duì)應(yīng)的新舊版本實(shí)現(xiàn)方式步鉴。
- refs通過React.createRef()創(chuàng)建揪胃,使屬性ref附加到React元素上。Refs通常在
一個(gè)組件構(gòu)造時(shí)賦值給一個(gè)實(shí)例屬性
氛琢,這樣在整個(gè)組件中他們都可以被引用到喊递。 - 創(chuàng)建以后,通過React.createRef().current方法獲取阳似。
根據(jù)節(jié)點(diǎn)類型的不同骚勘,ref的值也不同:
- 如果ref用在HTML元素上,構(gòu)造函數(shù)中通過React.createRef()創(chuàng)建的ref會(huì)將原生DOM元素放到它的current屬性中。
- 如果ref用在自定義組件類型上俏讹,ref使用它的current屬性指向所掛載的組件實(shí)例当宴。
- 函數(shù)式組件上不能使用ref,因?yàn)樗鼈儧]有實(shí)例。
3. DOM中創(chuàng)建與使用
class Input extends React.Component {
constructor(props) {
super(props)
// 在構(gòu)造方法內(nèi)初始化
this.inputRef = React.createRef()
}
componentDidMount() {
// 使用.current調(diào)用
this.inputRef.current.focus();
}
// Input的實(shí)例方法
focus = () => {
if(this.inputRef.current) this.inputRef.current.focus();
}
render() {
return (
<div className="block">
<p>Input 加載時(shí)自動(dòng)聚焦</p>
<input ref={this.inputRef} />
</div>
)
}
}
組件掛載時(shí)泽疆,React會(huì)將ref的current屬性設(shè)置成DOM元素户矢,卸載時(shí),再把ref的current屬性設(shè)置為null于微。ref更新發(fā)生在componentDidMount或者componentDidUpdate生命周期回調(diào)之前逗嫡。
4. 自定義組件中創(chuàng)建與使用
import React from 'react'
import Button from './Button'
import Input from './Input'
class Ref extends React.Component {
constructor(props) {
super(props)
// 初始化 獲取掛載的組件Input實(shí)例
this.inputComponentRef = React.createRef()
}
handleClick = () => {
// 調(diào)用Input實(shí)例的方法
if(this.inputComponentRef.current) this.inputComponentRef.current.focus()
}
render() {
return (
<div>
<Button onClick={this.handleClick} />
<Input ref={this.inputComponentRef} />
</div>
)
}
}
export default Ref
同DOM中使用類似,組件掛載時(shí)株依,React會(huì)將ref的current屬性設(shè)置成組件的實(shí)例驱证,卸載時(shí),再把ref的current屬性設(shè)置為null恋腕。ref更新發(fā)生在componentDidMount或者componentDidUpdate生命周期回調(diào)之前抹锄。
5. 函數(shù)式組件無法為當(dāng)前組件直接創(chuàng)建refs
const Input = () => <input />
class App extends React.Component {
constructor(props) {
super(props);
this.inputComponentRef = React.createRef();
}
render() {
// 不起作用,會(huì)報(bào)錯(cuò)
return (
<Input ref={this.inputComponentRef} />
);
}
}
但是荠藤,函數(shù)式組件內(nèi)部可以使用ref引用屬性使其指向一個(gè)DOM元素或者一個(gè)類組件
伙单,例如:
const Input = (props) => {
let inputRef = React.createRef();
function handleClick() {
inputRef.current.focus();
}
return (
<div>
<input
type="text"
ref={inputRef} />
<button
onClick={handleClick}
>Focus</button>
</div>
)
}
6. 使用回調(diào)的方式 (推薦)
在需要聲明ref的位置綁定一個(gè)方法,返回的參數(shù)是DOM節(jié)點(diǎn)或則實(shí)例組件哈肖,組件在加載時(shí)會(huì)自動(dòng)觸發(fā)該回調(diào)方法吻育,該參數(shù)作為實(shí)例的一個(gè)屬性在其他位置直接使用即可。
class Input extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
// 不需要使用current調(diào)用
this.inputRef && this.inputRef.focus();
}
initRef = (ele) => {
// 組件加載時(shí)(或者更新時(shí))自動(dòng)觸發(fā)該方法
this.inputRef = ele
}
focus = () => {
if(this.inputRef) this.inputRef.focus();
}
render() {
return (
<div className="block">
<p>Input 加載時(shí)自動(dòng)聚焦</p>
<input ref={this.initRef} />
</div>
)
}
}
export default Input
使用引用回調(diào)函數(shù)的注意事項(xiàng)
如果ref回調(diào)函數(shù)定義在內(nèi)聯(lián)函數(shù)(inline function)中淤井,更新時(shí)他會(huì)被調(diào)用兩次布疼,第一次參數(shù)是null,第二次參數(shù)才是DOM元素。這是因?yàn)槊總€(gè)渲染都會(huì)創(chuàng)建一個(gè)新的函數(shù)實(shí)例币狠,所以React需要清除舊的引用并設(shè)置新的游两。你可以通過將引用回調(diào)定義為該類的綁定方法來避免這種情況,但請(qǐng)注意漩绵,大多數(shù)情況下這樣做或者不這樣做都沒太大關(guān)系贱案。
7. React低版本遺留的API:字符串引用Refs
綁定一個(gè) 字符串類型的ref 屬性到 render 的返回值上
<input ref="myInput" />
在其他位置(實(shí)例方法或者生命周期函數(shù)中)使用
componentDidMount() {
// 保留關(guān)鍵字this.refs
// 頁面加載完成時(shí)使input標(biāo)簽自動(dòng)聚焦
this.refs.myInput.focus()
}
8. 建議使用其他的解決方案替代refs
在極少的一些情況下,我們需要從父組件中訪問某個(gè)子DOM節(jié)點(diǎn)或者子組件的一些屬性和方法止吐。一般來說不建議這么做宝踪,因?yàn)樗蚱屏私M件封裝,但是它偶爾也很有用祟印,比如觸發(fā)獲取焦點(diǎn)肴沫,或者測量一個(gè)子DOM節(jié)點(diǎn)的尺寸或者位置。
本文代碼鏈接地址:https://github.com/zhiyuanMain/ReactForJianshu.git