?
This document uses PHP Chinese website manual Release
在典型的React數(shù)據(jù)流中,道具是父組件與子項(xiàng)交互的唯一方式。要修改一個(gè)孩子,你需要用新的道具重新渲染它。但是,在少數(shù)情況下,您需要在典型數(shù)據(jù)流之外強(qiáng)制修改子級(jí)。要修改的孩子可以是React組件的一個(gè)實(shí)例,也可以是DOM元素。對(duì)于這兩種情況,React都提供了逃生艙口。
對(duì)于refs有幾個(gè)很好的用例:
管理焦點(diǎn),文本選擇或媒體播放。
觸發(fā)命令式動(dòng)畫。
與第三方DOM庫集成。
避免將ref用于任何可以通過聲明完成的事情。
例如,不要在組件上暴露open()
和使用close()
方法,而要將prop Dialog
傳遞isOpen
給它。
你的第一個(gè)傾向可能是使用引用來在應(yīng)用程序中“發(fā)生事情”。如果是這種情況,請(qǐng)花一點(diǎn)時(shí)間,更仔細(xì)地考慮組件層次結(jié)構(gòu)中應(yīng)該擁有哪些狀態(tài)。通常情況下,很顯然,“擁有”該州的適當(dāng)位置在層次結(jié)構(gòu)中處于較高水平。有關(guān)這方面的示例,請(qǐng)參閱提升狀態(tài)指南。
React支持可以附加到任何組件的特殊屬性。該ref
屬性采用回調(diào)函數(shù),并且在組件掛載或卸載后立即執(zhí)行回調(diào)。
當(dāng)在ref
HTML元素上使用該屬性時(shí),該ref
回調(diào)接收基礎(chǔ)DOM元素作為其參數(shù)。例如,此代碼使用ref
回調(diào)來存儲(chǔ)對(duì)DOM節(jié)點(diǎn)的引用:
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)組件裝入時(shí),React將使用DOM元素調(diào)用回調(diào),并null
在卸載時(shí)調(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
聲明為類的自定義組件上使用該屬性時(shí),ref
回調(diào)接收組件的已裝入實(shí)例作為其參數(shù)。例如,如果我們想要包裝CustomTextInput
上述內(nèi)容以模擬安裝后立即點(diǎn)擊它:
class AutoFocusTextInput extends React.Component { componentDidMount() { this.textInput.focusTextInput(); } render() { return ( <CustomTextInput ref={(input) => { this.textInput = input; }} /> ); } }
請(qǐng)注意,這僅適用于CustomTextInput
聲明為類的情況:
class CustomTextInput extends React.Component { // ...}
您不能 在功能組件上使用該屬性,因?yàn)樗鼈儧]有實(shí)例: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)時(shí)一樣。
但是,只要您引用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é)點(diǎn)的DOM節(jié)點(diǎn)。通常不建議這樣做,因?yàn)樗蚱屏私M件封裝,但它偶爾可用于觸發(fā)焦點(diǎn)或測(cè)量子DOM節(jié)點(diǎn)的大小或位置。
雖然您可以向子組件添加ref,但這不是理想的解決方案,因?yàn)槟粫?huì)獲取組件實(shí)例而不是DOM節(jié)點(diǎn)。此外,這不適用于功能組件。
相反,在這種情況下,我們建議在兒童身上暴露特殊的道具。這個(gè)孩子會(huì)使用一個(gè)任意名稱(例如inputRef
)的函數(shù)道具,并將其作為一個(gè)ref
屬性附加到DOM節(jié)點(diǎn)。這可以讓父母通過中間組件將其ref回調(diào)傳遞給子節(jié)點(diǎn)的DOM節(jié)點(diǎn)。
這適用于類和功能組件。
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)作為一個(gè)inputRef
道具傳遞給CustomTextInput
,并且CustomTextInput
將相同的函數(shù)作為特殊ref
屬性傳遞給<input>
。其結(jié)果是,this.inputElement
在Parent
將被設(shè)置為對(duì)應(yīng)于所述DOM節(jié)點(diǎn)<input>
中的元素CustomTextInput
。
請(qǐng)注意,inputRef
上例中prop 的名稱沒有特殊含義,因?yàn)樗浅R?guī)組件prop。然而,使用ref
屬性<input>
本身很重要,因?yàn)樗嬖VReact將ref附加到它的DOM節(jié)點(diǎn)。
即使它CustomTextInput
是一個(gè)功能組件,它也可以工作。與ref
只能為DOM元素和類組件指定的特殊屬性不同,對(duì)常規(guī)組件道具沒有限制inputRef
。
這種模式的另一個(gè)好處是它可以深入地處理多個(gè)組件。例如,假設(shè)Parent
不需要該DOM節(jié)點(diǎn),但是呈現(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
作為一個(gè)常規(guī)道具稱為inputRef
,Parent
并將其CustomTextInput
作為道具傳遞給它。最后,CustomTextInput
讀取inputRef
prop并將傳遞的函數(shù)作為ref
屬性附加到<input>
。其結(jié)果是,this.inputElement
在Grandparent
將被設(shè)置為對(duì)應(yīng)于所述DOM節(jié)點(diǎn)<input>
中的元素CustomTextInput
。
所有事情都考慮到了,我們建議盡可能暴露DOM節(jié)點(diǎn),但這可能是一個(gè)有用的逃生孵化器。請(qǐng)注意,這種方法需要您向子組件添加一些代碼。如果你完全不能控制子組件的實(shí)現(xiàn),你最后的選擇是使用findDOMNode()
,但不鼓勵(lì)。
如果你之前使用過React,那么你可能會(huì)熟悉一個(gè)較老的API,其中ref
屬性是一個(gè)字符串"textInput"
,并且DOM節(jié)點(diǎn)被訪問為this.refs.textInput
。我們建議不要這樣做,因?yàn)樽址糜幸恍﹩栴},被認(rèn)為是遺留問題,并且可能會(huì)在將來的某個(gè)版本中刪除。如果您當(dāng)前正在使用this.refs.textInput
訪問參考,我們建議使用回調(diào)模式。
如果ref
回調(diào)被定義為一個(gè)內(nèi)聯(lián)函數(shù),它將在更新期間被調(diào)用兩次,首先null
是DOM元素,然后再一次。這是因?yàn)槊總€(gè)渲染都會(huì)創(chuàng)建一個(gè)新的函數(shù)實(shí)例,所以React需要清除舊的參考并設(shè)置新的實(shí)例。您可以通過將ref
回調(diào)定義為該類的綁定方法來避免這種情況,但請(qǐng)注意,在大多數(shù)情況下它不應(yīng)該存在問題。