React?Refs?的使用forwardRef?源碼示例解析
三種使用方式
React 提供了 Refs,幫助我們訪問 DOM 節(jié)點(diǎn)或在 render 方法中創(chuàng)建的 React 元素。
React 提供了三種使用 Ref 的方式:
1. String Refs
class App extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
setTimeout(() => {
// 2. 通過 this.refs.xxx 獲取 DOM 節(jié)點(diǎn)
this.refs.textInput.value = 'new value'
}, 2000)
}
render() {
// 1. ref 直接傳入一個(gè)字符串
return (
<div>
<input ref="textInput" value='value' />
</div>
)
}
}
root.render(<App />);
2. 回調(diào) Refs
class App extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
setTimeout(() => {
// 2. 通過實(shí)例屬性獲取 DOM 節(jié)點(diǎn)
this.textInput.value = 'new value'
}, 2000)
}
render() {
// 1. ref 傳入一個(gè)回調(diào)函數(shù)
// 該函數(shù)中接受 React 組件實(shí)例或 DOM 元素作為參數(shù)
// 我們通常會(huì)將其存儲(chǔ)到具體的實(shí)例屬性(this.textInput)
return (
<div>
<input ref={(element) => {
this.textInput = element;
}} value='value' />
</div>
)
}
}
root.render(<App />);
3. createRef
class App extends React.Component {
constructor(props) {
super(props)
// 1. 使用 createRef 創(chuàng)建 Refs
// 并將 Refs 分配給實(shí)例屬性 textInputRef,以便在整個(gè)組件中引用
this.textInputRef = React.createRef();
}
componentDidMount() {
setTimeout(() => {
// 3. 通過 Refs 的 current 屬性進(jìn)行引用
this.textInputRef.current.value = 'new value'
}, 2000)
}
render() {
// 2. 通過 ref 屬性附加到 React 元素
return (
<div>
<input ref={this.textInputRef} value='value' />
</div>
)
}
}
這是最被推薦使用的方式。
兩種使用目的
Refs 除了用于獲取具體的 DOM 節(jié)點(diǎn)外,也可以獲取 Class 組件的實(shí)例,當(dāng)獲取到實(shí)例后,可以調(diào)用其中的方法,從而強(qiáng)制執(zhí)行,比如動(dòng)畫之類的效果。
我們舉一個(gè)獲取組件實(shí)例的例子:
class Input extends React.Component {
constructor(props) {
super(props)
this.textInputRef = React.createRef();
}
handleFocus() {
this.textInputRef.current.focus();
}
render() {
return <input ref={this.textInputRef} value='value' />
}
}
class App extends React.Component {
constructor(props) {
super(props)
this.inputRef = React.createRef();
}
componentDidMount() {
setTimeout(() => {
this.inputRef.current.handleFocus()
}, 2000)
}
render() {
return (
<div>
<Input ref={this.inputRef} value='value' />
</div>
)
}
}
在這個(gè)例子中,我們通過 this.inputRef.current 獲取到 Input 組件的實(shí)例,并調(diào)用了實(shí)例的 handleFocus 方法,在這個(gè)方法中,又通過 Refs 獲取到具體的 DOM 元素,執(zhí)行了 focus 原生方法。
Refs 轉(zhuǎn)發(fā)
有的時(shí)候,我們開發(fā)一個(gè)組件,這個(gè)組件需要對組件使用者提供一個(gè) ref 屬性,用于讓組件使用者獲取具體的 DOM 元素,我們就需要進(jìn)行 Refs 轉(zhuǎn)發(fā),這對于 class 組件并不是一個(gè)問題,舉個(gè)示例代碼:
class Child extends React.Component {
render() {
const {inputRef, ...rest} = this.props;
// 3. 這里將 props 中的 inputRef 賦給 DOM 元素的 ref
return <input ref={inputRef} {...rest} placeholder="value" />
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
// 1. 創(chuàng)建 refs
this.inputRef = React.createRef();
}
componentDidMount() {
setTimeout(() => {
// 4. 使用 this.inputRef.current 獲取子組件中渲染的 DOM 節(jié)點(diǎn)
this.inputRef.current.value = 'new value'
}, 2000)
}
render() {
// 2. 因?yàn)?ref 屬性不能通過 this.props 獲取,所以這里換了一個(gè)屬性名
return <Child inputRef={this.inputRef} />
}
}
但對于函數(shù)式組件,這卻是一個(gè)問題。
我們是不能在函數(shù)組件上使用 ref 屬性的,因?yàn)楹瘮?shù)組件沒有實(shí)例。
所以 React 提供了 forwardRef 這個(gè) API,我們直接看使用示例:
// 3. 子組件通過 forwardRef 獲取 ref,并通過 ref 屬性綁定 React 元素
const Child = forwardRef((props, ref) => (
<input ref={ref} placeholder="value" />
));
class Parent extends React.Component {
constructor(props) {
super(props)
// 1. 創(chuàng)建 refs
this.inputRef = React.createRef();
}
componentDidMount() {
setTimeout(() => {
// 4. 使用 this.inputRef.current 獲取子組件中渲染的 DOM 節(jié)點(diǎn)
this.inputRef.current.value = 'new value'
}, 2000)
}
render() {
// 2. 傳給子組件的 ref 屬性
return <Child ref={this.inputRef} />
}
}
尤其是在我們編寫高階組件的時(shí)候,往往要實(shí)現(xiàn) refs 轉(zhuǎn)發(fā)。我們知道,一個(gè)高階組件,會(huì)接受一個(gè)組件,返回一個(gè)包裹后的新組件,從而實(shí)現(xiàn)某種功能的增強(qiáng)。
但也正是如此,我們添加 ref,獲取的會(huì)是包裹后的新組件的實(shí)例,而非被包裹的組件實(shí)例,這就可能會(huì)導(dǎo)致一些問題。
createRef 源碼
現(xiàn)在我們看下 createRef 的源碼,源碼的位置在 /packages/react/src/ReactCreateRef.js,代碼其實(shí)很簡單,就只是返回了一個(gè)具有 current 屬性的對象:
// 簡化后
export function createRef() {
const refObject = {
current: null,
};
return refObject;
}
在渲染的過程中,refObject.current 會(huì)被賦予具體的值。
forwardRef 源碼
那 forwardRef 源碼呢?源碼的位置在 /packages/react/src/ReactForwardRef.js,代碼也很簡單:
// 簡化后
const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');
export function forwardRef(render) {
const elementType = {
$$typeof: REACT_FORWARD_REF_TYPE,
render,
};
return elementType;
}
但是要注意這里的 $$typeof,盡管這里是 REACT_FORWARD_REF_TYPE,但最終創(chuàng)建的 React 元素的 $$typeof 依然為 REACT_ELEMENT_TYPE。
關(guān)于 createElement 的源碼分析參考 《React 之 createElement 源碼解讀》,我們這里簡單分析一下,以 InputComponent 為例:
// 使用 forwardRef
const InputComponent = forwardRef(({value}, ref) => (
<input ref={ref} className="FancyButton" value={value} />
));
// 根據(jù) forwardRef 的源碼,最終返回的對象格式為:
const InputComponent = {
$$typeof: REACT_FORWARD_REF_TYPE,
render,
}
// 使用組件
const result = <InputComponent />
// Bable 將其轉(zhuǎn)譯為:
const result = React.createElement(InputComponent, null);
// 最終返回的對象為:
const result = {
$$typeof: REACT_ELEMENT_TYPE,
type: {
$$typeof: REACT_FORWARD_REF_TYPE,
render,
}
}
我們嘗試著打印一下最終返回的對象,確實(shí)也是這樣的結(jié)構(gòu):

React 系列
以上就是React 之 Refs 的使用和 forwardRef 的源碼解讀的詳細(xì)內(nèi)容,更多關(guān)于React Refs使用forwardRef 的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
react中實(shí)現(xiàn)修改input的defaultValue
這篇文章主要介紹了react中實(shí)現(xiàn)修改input的defaultValue方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
react項(xiàng)目中redux的調(diào)試工具不起作用的解決
這篇文章主要介紹了react項(xiàng)目中redux的調(diào)試工具不起作用的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
react-intl實(shí)現(xiàn)React國際化多語言的方法
這篇文章主要介紹了react-intl實(shí)現(xiàn)React國際化多語言的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
高性能React開發(fā)React Server Components詳解
ReactServerComponents通過服務(wù)器端渲染、自動(dòng)代碼分割等技術(shù),實(shí)現(xiàn)了高性能的React開發(fā),它解決了客戶端數(shù)據(jù)請求鏈?zhǔn)窖舆t、敏感數(shù)據(jù)暴露風(fēng)險(xiǎn)等問題,提供了更好的用戶體驗(yàn)和安全性,本文介紹高性能React開發(fā)React Server Components詳解,感興趣的朋友一起看看吧2025-03-03
React實(shí)現(xiàn)antdM的級(jí)聯(lián)菜單實(shí)例
這篇文章主要為大家介紹了React實(shí)現(xiàn)antdM的級(jí)聯(lián)菜單實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10

