refs提供了一種方法薪棒,用于訪問在render中創(chuàng)建的dom節(jié)點或者React元素。
在典型的React數(shù)據(jù)流中斥铺,屬性(props)
是父組件與子組件交互的唯一方式贬派。要修改子組件急但,你需要使用新的props重新渲染他。
但是某些情況下搞乏,你需要在典型的數(shù)據(jù)流外波桩,強制修改子組件。要修改的子組件可以是React的實例请敦,也可以是DOM元素镐躲,對于這兩種情況,React提供了解決辦法侍筛。
何時使用Refs
①處理焦點萤皂,文本選擇或者媒體控制
②觸發(fā)強制動畫
③集成第三方的DOM庫
如果可以通過聲明方式實現(xiàn),盡量避免使用Refs
例如匣椰,不要在 Dialog
組件上直接暴露 open()
和 close()
方法裆熙,最好傳遞 isOpen
屬性。
不要過度使用Refs
你可能首先會想到在應(yīng)用中使用Refs
來更新組件禽笑,如果是這種情況入录,思考一下,state
屬性在組件層級中的位置佳镜,通常你會想明白僚稿,狀態(tài)提升:提升組件state
所在層級會是更加合適的辦法。有關(guān)示例蟀伸,請參考狀態(tài)提升.
Note
下面的例子已經(jīng)用 React v16.3 引入的 React.createRef()
API 更新蚀同。如果你正在使用 React 更早的發(fā)布版,我們推薦使用回調(diào)形式的 refs望蜡。
創(chuàng)建Refs
①使用React.creatRef()來創(chuàng)建refs唤崭,通過ref屬性來獲得React元素。
②當(dāng)構(gòu)造組件時脖律,refs通常被賦值給組件的一個屬性谢肾。這樣可以在組件的任意一處使用他。
class MyComponent extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
render(){
return <div ref={this.myRef}></div>
}
}
訪問refs
當(dāng)一個ref
屬性被傳遞給一個render()
函數(shù)中的元素時小泉,可以使用ref
中的current
屬性對節(jié)點的引用進行訪問芦疏。
const current = this.myRef.current;
ref
的值取決于節(jié)點類型
①當(dāng) ref
屬性被用于一個普通的 HTML 元素時,React.createRef()
將接收底層 DOM 元素作為它的 current
屬性以創(chuàng)建 ref
微姊。
②當(dāng) ref
被用于一個自定義的類組件的時候酸茴,ref
對象將接收該組件已掛載的實例,作為他的current
兢交。
③我們不能在函數(shù)形式的組件上面使用ref
薪捍,因為他們是沒有實例的。
demo
以下的代碼存儲了ref
屬性對節(jié)點的引用
class CustomTextInput extends React.Component{
constructor(props){
super(props);
//創(chuàng)建ref存儲 textInput DOM元素
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput(){
//直接使用原生的api讓text獲得焦點
//這里使用current來獲取dom節(jié)點
this.textInput.current.focus();
}
render(){
//這里告訴React 我們把 input ref 關(guān)聯(lián)到構(gòu)造器里面創(chuàng)建的 textInput 上
return(
<div>
<input type="text" ref={this.textInput} />
<input type="text" type="button" value="focus ths text"
onClick={this.focusTextInput} />
</div>
)
}
}
ReactDOM.render(
<CustomTextInput />,document.getElementById('root')
)
React會在組件加載時將DOM元素傳入current
屬性,在組件卸載的時候回改成null
,ref
的更新會發(fā)生在componentDidMounted
和componentDidUpdate
的生命周期鉤子之前酪穿。
為類組件添加ref
如果我們想要包裝上面的CustomTextInput
凳干,來模擬掛載之后立即被點擊的話,我們可以使用ref
來訪問自定義輸入被济,并且手動調(diào)用他們的focusTextInput
方法救赐。
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.focusTextInput();
}
render() {
return (
<CustomTextInput ref={this.textInput} />
);
}
}
需要注意的是,這種方法只對于使用class
聲明的組件有效只磷。
Refs與函數(shù)式組件
我們不能在函數(shù)式組件上面使用ref
屬性经磅,因為在函數(shù)式組件上面沒有他們的實例。
如果你想使用ref
或者state
以及其他聲明周期方法的時候钮追,就需要將函數(shù)式組件轉(zhuǎn)化成class
聲明的組件预厌。
注意:我們可以在函數(shù)式組件內(nèi)部使用ref
,只要他指向一個DOM或者class
組件畏陕。
function CustomTextInput(props){
//這里必須聲明 textInput配乓,這樣ref的回調(diào)才能夠引用它
let textInput = null;
function handleClick(){
textInput.focus();
}
return (
<div>
<input type="text" ref={(input)=>{textInput=input}}/>
<input type="button" value="hello" onClick={handleClick} />
</div>
)
}
對父組件暴露DOM節(jié)點
在極少數(shù)的情況下, 你可能希望通過父組件訪問子組件的節(jié)點惠毁。通常是不推薦這樣做的犹芹,因為這樣會破壞組件的封裝。但是偶爾也可以用于觸發(fā)焦點后者測量子節(jié)點dom大小或者位置鞠绰。
雖然你可以向子組件添加 ref腰埂,但是這不是一個理想的解決方案,因為只能獲取組件實例蜈膨,而不是dom節(jié)點屿笼,并且這方法在函數(shù)式組件上是無效的。
如果你使用 React 16.3 或更高, 這種情況下我們推薦使用 ref 轉(zhuǎn)發(fā)翁巍。
Ref轉(zhuǎn)發(fā)可以讓組件像暴露自己的ref一樣暴露子組件的ref驴一,關(guān)于怎樣對父組件暴露子組件的 DOM 節(jié)點,在 ref 轉(zhuǎn)發(fā)文檔 中有一個詳細的例子灶壶。
如果你使用 React 16.2 或更低肝断,或者你需要比 ref 轉(zhuǎn)發(fā)更高的靈活性,你可以使用 這個替代方案 將 ref 作為特殊名字的 prop 直接傳遞驰凛。
可能的話胸懈,我們不建議暴露 DOM 節(jié)點,但有時候它會成為救命稻草恰响。注意這些方案需要你在子組件中增加一些代碼趣钱。如果你對子組件的實現(xiàn)沒有控制權(quán)的話,你剩下的選擇是使用 findDOMNode()
胚宦,但是不推薦首有。
回調(diào)refs
React也支持另一種設(shè)置ref
的方式燕垃,稱為回調(diào)ref
,這可以更加細致的控制什么時候設(shè)置和移除 ref
绞灼。
這不同于創(chuàng)建createRef
創(chuàng)建的ref
屬性利术,“回調(diào) ref”會傳遞一個函數(shù),這個函數(shù)接收React組件的實例低矮,或者html中的dom元素作為參數(shù),來存儲他們以便于被其他地方來訪問被冒。
demo:
使用ref
回調(diào)函數(shù)军掂,在實例的屬性中,存儲對dom節(jié)點的應(yīng)用昨悼。
class CustomTextInput extends React.Component{
constructor(props){
super(props);
this.textInput = null;
this.setTextInputRef=element=>{
this.textInput=element;
}
this.focusTextInput=()=>{
if(this.textInput) this.textInput.focus();
}
}
componentDidMount(){
// 渲染后文本框自動獲得焦點
this.focusTextInput();
}
render(){
//使用ref的回調(diào)將text輸入框的dom節(jié)點存儲到react
return(
<div>
<input type="text" ref={this.setTextInputRef} />
<input type="button" value=""focus the test onClick={this.focusTextInput} />
</div>
)
}
}
React將在組件掛載時蝗锥,將dom元素傳入ref
的回調(diào)函數(shù)并且調(diào)用,在卸載的時候傳入null
并且調(diào)用他率触。
ref
的回調(diào)函數(shù)會在componentDidMount
和componentDidUpdate
的生命周期函數(shù)被調(diào)用之前终议。
我們可以在組件之間傳遞回調(diào)形式的refs
,就像你可以傳遞React.createRef()
創(chuàng)建的refs
對象一樣葱蝗。
function CustomTextInput(props){
return(
<div>
<input type="text" ref={props.inputRef}/>
</div>
)
}
class Parent extends React.Component{
render(){
return(
<CustomTextInput inputRef={el=>this.inputElement=el} />
)
}
}
在上面的例子中穴张,Parent 傳遞給它的 ref 回調(diào)函數(shù)作為 inputRef 傳遞給 CustomTextInput,然后 CustomTextInput 通過 ref屬性將其傳遞給 <input>两曼。最終皂甘,Parent 中的 this.inputElement 將被設(shè)置為與 CustomTextIput 中的 <input> 元素相對應(yīng)的 DOM 節(jié)點
注意
如果 ref 回調(diào)以內(nèi)聯(lián)函數(shù)的方式定義,在更新期間它會被調(diào)用兩次悼凑,第一次參數(shù)是 null 偿枕,之后參數(shù)是 DOM 元素。這是因為在每次渲染中都會創(chuàng)建一個新的函數(shù)實例户辫。因此渐夸,React 需要清理舊的 ref 并且設(shè)置新的。通過將 ref 的回調(diào)函數(shù)定義成類的綁定函數(shù)的方式可以避免上述問題渔欢,但是大多數(shù)情況下無關(guān)緊要墓塌。