React 源碼閱讀-2
在典型的React數據流中,props是父組件與其子組件交互的唯一方式。要修改子項,請使用new props 重新呈現它。但是,在某些情況下,需要在典型數據流之外強制修改子項。要修改的子項可以是React組件的實例,也可以是DOM元素。對於這兩種情況,React都提供了api。
何時使用refs
refs
有一些很好的用例:
- 1.文本選擇或媒體播放。
- 2.觸發勢在必行的動畫。
- 3.與第三方DOM庫集成。
避免將refs
用於可以聲明性地完成的任何操作。
不要過度使用Refs
舊版API
:字符串引用
如果您之前使用過React
,那麼您可能熟悉一箇舊的API
,其中ref
屬性是一個字符串textInput
,並且DOM節點被訪問爲this.refs.textInput
。建議不要使用它,因爲字符串引用有一些問題,被認爲是遺留問題,很可能會在未來的某個版本中刪除。
回調引用
當組件安裝時,React
將使用DOM
元素調用ref
回調,並在卸載時調用null
。
在componentDidMoun
t或componentDidUpdate
觸發之前,Refs
保證是最新的.
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.focusTextInput = () => {
// Focus the text input using the raw DOM API
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// autofocus the input on mount
this.focusTextInput();
}
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={element => this.textInput = element}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
關於回調 refs
的說明
如果 ref
回調函數是以內聯函數的方式定義的,在更新過程中它會被執行兩次,第一次傳入參數 null,然後第二次會傳入參數 DOM 元素。這是因爲在每次渲染時會創建一個新的函數實例,所以React
清空舊的 ref
並且設置新的。通過將 ref
的回調函數定義成 class
的綁定函數的方式可以避免上述問題,但是大多數情況下它是無關緊要的。
refs
例子--點擊獲取input
焦點
class Example extends React.Component {
handleClick() {
// 使用原生的 DOM API 獲取焦點
this.refs.myInput.focus
();
}
render() {
// 當組件插入到 DOM 後,ref 屬性添加一個組件的引用於到 this.refs
return (
<div>
<input type="text" ref="myInput" />
<input
type="button"
value="點我輸入框獲取焦點"
onClick={this.handleClick.bind(this)}
/>
</div>
);
}
}
使用React.createRef()
React.createRef()React 16.3中引入的API。如果您使用的是早期版本的React,我們建議您使用回調引用。
創建React.createRef()
Refs
是使用屬性創建的,React.createRef()
並通過ref
屬性附加到React
元素。在構造組件時,通常將Refs
分配給實例屬性,以便可以在整個組件中引用它們。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
訪問參考
當ref
被傳遞給元素時render
,對該節點的引用變得可以在currentref
的屬性處訪問
const node = this.myRef.current;
ref的值根據節點的類型而有所不同
- 當在
refHTML
元素上使用該屬性時,ref在構造函數中創建的屬性將React.createRef()
接收底層DOM元素作爲其current屬性。 - 在
ref
自定義類組件上使用該屬性時,該ref
對象將接收組件的已安裝實例作爲其current
。
您可能無法ref
在函數組件上使用該屬性,因爲它們沒有實例。
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
}
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
current當組件安裝時,React將爲該屬性分配DOM元素,並null在卸載時將其分配回。ref更新發生之前componentDidMount或componentDidUpdate生命週期方法。
源碼
// an immutable object with a single mutable value
export function createRef(): RefObject {
const refObject = {
current: null,
};
if (__DEV__) {
Object.seal(refObject);
}
return refObject;
}
無法ref在函數組件上使用該屬性
function MyFunctionComponent() {
return <input />;
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
// This will *not* work!
return (
<MyFunctionComponent ref={this.textInput} />
);
}
}
**如果需要引用它,則應該將組件轉換爲類,就像您需要生命週期方法或狀態時一樣。
但是,只要引用DOM元素或類組件,就可以在函數組件中使用該ref
屬性:
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
let textInput = React.createRef();
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
將DOM引用公開給父組件
在極少數情況下,可能希望從父組件訪問子節點的DOM節點。通常不建議這樣做,因爲它會破壞組件封裝,但它偶爾可用於觸發焦點或測量子DOM節點的大小或位置。
雖然可以向子組件添加引用,但這不是一個理想的解決方案,因爲只能獲得組件實例而不是DOM節點。此外,這不適用於功能組件。
如果您使用React 16.3或更高版本,我們建議在這些情況下使用ref forwarding。引用轉發允許組件選擇將任何子組件的引用公開爲自己的組件。可以在ref轉發文檔中找到有關如何將子DOM節點公開給父組件的詳細示例。
如果您使用React 16.2或更低版本,或者您需要比ref轉發提供的更多靈活性,您可以使用此替代方法並明確地將ref作爲不同名稱的prop傳遞。
如果可能,建議不要暴露DOM節點,但它可以是一個有用的逃生艙。請注意,此方法要求您向子組件添加一些代碼。如果您完全無法控制子組件實現,則最後一個選項是使用findDOMNode(),但不鼓勵使用它StrictMode
。