React進階筆記3(Refs & DOM)

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ā)生在componentDidMountedcomponentDidUpdate的生命周期鉤子之前酪穿。

為類組件添加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ù)會在componentDidMountcomponentDidUpdate的生命周期函數(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)緊要墓塌。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市膘茎,隨后出現(xiàn)的幾起案子桃纯,更是在濱河造成了極大的恐慌,老刑警劉巖披坏,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件态坦,死亡現(xiàn)場離奇詭異,居然都是意外死亡棒拂,警方通過查閱死者的電腦和手機伞梯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門玫氢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谜诫,你說我怎么就攤上這事漾峡。” “怎么了喻旷?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵生逸,是天一觀的道長。 經(jīng)常有香客問我且预,道長槽袄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任锋谐,我火速辦了婚禮遍尺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涮拗。我一直安慰自己乾戏,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布三热。 她就那樣靜靜地躺著鼓择,像睡著了一般。 火紅的嫁衣襯著肌膚如雪康铭。 梳的紋絲不亂的頭發(fā)上惯退,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音从藤,去河邊找鬼催跪。 笑死,一個胖子當(dāng)著我的面吹牛夷野,可吹牛的內(nèi)容都是我干的懊蒸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼悯搔,長吁一口氣:“原來是場噩夢啊……” “哼骑丸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起妒貌,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤通危,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后灌曙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菊碟,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年在刺,在試婚紗的時候發(fā)現(xiàn)自己被綠了逆害。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片头镊。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖魄幕,靈堂內(nèi)的尸體忽然破棺而出相艇,到底是詐尸還是另有隱情,我是刑警寧澤纯陨,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布坛芽,位于F島的核電站,受9級特大地震影響翼抠,放射性物質(zhì)發(fā)生泄漏靡馁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一机久、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赔嚎,春花似錦膘盖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至损晤,卻和暖如春软棺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尤勋。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工喘落, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人最冰。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓瘦棋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親暖哨。 傳聞我的和親對象是個殘疾皇子赌朋,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容