?
This document uses PHP Chinese website manual Release
在典型的React數(shù)據(jù)流中,道具是父組件與子項交互的唯一方式。要修改一個孩子,你需要用新的道具重新渲染它。但是,在少數(shù)情況下,您需要在典型數(shù)據(jù)流之外強(qiáng)制修改子級。要修改的孩子可以是React組件的一個實例,也可以是DOM元素。對于這兩種情況,React都提供了逃生艙口。
對于refs有幾個很好的用例:
管理焦點,文本選擇或媒體播放。
觸發(fā)命令式動畫。
與第三方DOM庫集成。
避免將ref用于任何可以通過聲明完成的事情。
例如,不要在組件上暴露open()
和使用close()
方法,而要將prop Dialog
傳遞isOpen
給它。
你的第一個傾向可能是使用引用來在應(yīng)用程序中“發(fā)生事情”。如果是這種情況,請花一點時間,更仔細(xì)地考慮組件層次結(jié)構(gòu)中應(yīng)該擁有哪些狀態(tài)。通常情況下,很顯然,“擁有”該州的適當(dāng)位置在層次結(jié)構(gòu)中處于較高水平。有關(guān)這方面的示例,請參閱提升狀態(tài)指南。
React支持可以附加到任何組件的特殊屬性。該ref
屬性采用回調(diào)函數(shù),并且在組件掛載或卸載后立即執(zhí)行回調(diào)。
當(dāng)在ref
HTML元素上使用該屬性時,該ref
回調(diào)接收基礎(chǔ)DOM元素作為其參數(shù)。例如,此代碼使用ref
回調(diào)來存儲對DOM節(jié)點的引用:
class CustomTextInput extends React.Component { constructor(props) { super(props); this.focusTextInput = this.focusTextInput.bind(this); } focusTextInput() { // Explicitly focus the text input using the raw DOM API this.textInput.focus(); } render() { // Use the `ref` callback to store a reference to the text input DOM // element in an instance field (for example, this.textInput). return ( <div> <input type="text" ref={(input) => { this.textInput = input; }} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); }}
ref
當(dāng)組件裝入時,React將使用DOM元素調(diào)用回調(diào),并null
在卸載時調(diào)用它。ref
回調(diào)之前被調(diào)用componentDidMount
或componentDidUpdate
生命周期鉤。
使用ref
回調(diào)來設(shè)置類的屬性是訪問DOM元素的常用模式。首選的方法是在ref
回調(diào)中設(shè)置屬性,如上例所示。甚至有更短的寫法:ref={input => this.textInput = input}
。
當(dāng)在ref
聲明為類的自定義組件上使用該屬性時,ref
回調(diào)接收組件的已裝入實例作為其參數(shù)。例如,如果我們想要包裝CustomTextInput
上述內(nèi)容以模擬安裝后立即點擊它:
class AutoFocusTextInput extends React.Component { componentDidMount() { this.textInput.focusTextInput(); } render() { return ( <CustomTextInput ref={(input) => { this.textInput = input; }} /> ); } }
請注意,這僅適用于CustomTextInput
聲明為類的情況:
class CustomTextInput extends React.Component { // ...}
您不能 在功能組件上使用該屬性,因為它們沒有實例:ref
function MyFunctionalComponent() { return <input />;} class Parent extends React.Component { render() { // This will *not* work! return ( <MyFunctionalComponent ref={(input) => { this.textInput = input; }} /> ); } }
如果需要引用組件,則應(yīng)該將組件轉(zhuǎn)換為類,就像在需要生命周期方法或狀態(tài)時一樣。
但是,只要您引用DOM元素或類組件,就可以在功能組件內(nèi)使用該 屬性:ref
function CustomTextInput(props) { // textInput must be declared here so the ref callback can refer to it let textInput = null; function handleClick() { textInput.focus(); } return ( <div> <input type="text" ref={(input) => { textInput = input; }} /> <input type="button" value="Focus the text input" onClick={handleClick} /> </div> ); }
在極少數(shù)情況下,您可能想要從父組件訪問子節(jié)點的DOM節(jié)點。通常不建議這樣做,因為它打破了組件封裝,但它偶爾可用于觸發(fā)焦點或測量子DOM節(jié)點的大小或位置。
雖然您可以向子組件添加ref,但這不是理想的解決方案,因為您只會獲取組件實例而不是DOM節(jié)點。此外,這不適用于功能組件。
相反,在這種情況下,我們建議在兒童身上暴露特殊的道具。這個孩子會使用一個任意名稱(例如inputRef
)的函數(shù)道具,并將其作為一個ref
屬性附加到DOM節(jié)點。這可以讓父母通過中間組件將其ref回調(diào)傳遞給子節(jié)點的DOM節(jié)點。
這適用于類和功能組件。
function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> );} class Parent extends React.Component { render() { return ( <CustomTextInput inputRef={el => this.inputElement = el} /> ); } }
在上面的例子中,Parent
將它的ref回調(diào)作為一個inputRef
道具傳遞給CustomTextInput
,并且CustomTextInput
將相同的函數(shù)作為特殊ref
屬性傳遞給<input>
。其結(jié)果是,this.inputElement
在Parent
將被設(shè)置為對應(yīng)于所述DOM節(jié)點<input>
中的元素CustomTextInput
。
請注意,inputRef
上例中prop 的名稱沒有特殊含義,因為它是常規(guī)組件prop。然而,使用ref
屬性<input>
本身很重要,因為它告訴React將ref附加到它的DOM節(jié)點。
即使它CustomTextInput
是一個功能組件,它也可以工作。與ref
只能為DOM元素和類組件指定的特殊屬性不同,對常規(guī)組件道具沒有限制inputRef
。
這種模式的另一個好處是它可以深入地處理多個組件。例如,假設(shè)Parent
不需要該DOM節(jié)點,但是呈現(xiàn)的組件Parent
(讓我們稱之為Grandparent
)需要訪問它。然后我們可以讓Grandparent
指定的inputRef
道具Parent
,并Parent
“轉(zhuǎn)發(fā)”到CustomTextInput
:
function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> ); } function Parent(props) { return ( <div> My input: <CustomTextInput inputRef={props.inputRef} /> </div> ); } class Grandparent extends React.Component { render() { return ( <Parent inputRef={el => this.inputElement = el} /> ); } }
這里,ref回調(diào)首先由指定Grandparent
。它被傳遞給Parent
作為一個常規(guī)道具稱為inputRef
,Parent
并將其CustomTextInput
作為道具傳遞給它。最后,CustomTextInput
讀取inputRef
prop并將傳遞的函數(shù)作為ref
屬性附加到<input>
。其結(jié)果是,this.inputElement
在Grandparent
將被設(shè)置為對應(yīng)于所述DOM節(jié)點<input>
中的元素CustomTextInput
。
所有事情都考慮到了,我們建議盡可能暴露DOM節(jié)點,但這可能是一個有用的逃生孵化器。請注意,這種方法需要您向子組件添加一些代碼。如果你完全不能控制子組件的實現(xiàn),你最后的選擇是使用findDOMNode()
,但不鼓勵。
如果你之前使用過React,那么你可能會熟悉一個較老的API,其中ref
屬性是一個字符串"textInput"
,并且DOM節(jié)點被訪問為this.refs.textInput
。我們建議不要這樣做,因為字符串引用有一些問題,被認(rèn)為是遺留問題,并且可能會在將來的某個版本中刪除。如果您當(dāng)前正在使用this.refs.textInput
訪問參考,我們建議使用回調(diào)模式。
如果ref
回調(diào)被定義為一個內(nèi)聯(lián)函數(shù),它將在更新期間被調(diào)用兩次,首先null
是DOM元素,然后再一次。這是因為每個渲染都會創(chuàng)建一個新的函數(shù)實例,所以React需要清除舊的參考并設(shè)置新的實例。您可以通過將ref
回調(diào)定義為該類的綁定方法來避免這種情況,但請注意,在大多數(shù)情況下它不應(yīng)該存在問題。