React?Refs?的使用forwardRef?源碼示例解析
三種使用方式
React 提供了 Refs,幫助我們?cè)L問(wèn) 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. 通過(guò) 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. 通過(guò)實(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. 通過(guò) Refs 的 current 屬性進(jìn)行引用 this.textInputRef.current.value = 'new value' }, 2000) } render() { // 2. 通過(guò) 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)畫(huà)之類(lèi)的效果。
我們舉一個(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è)例子中,我們通過(guò) this.inputRef.current
獲取到 Input 組件的實(shí)例,并調(diào)用了實(shí)例的 handleFocus 方法,在這個(gè)方法中,又通過(guò) Refs 獲取到具體的 DOM 元素,執(zhí)行了 focus 原生方法。
Refs 轉(zhuǎn)發(fā)
有的時(shí)候,我們開(kāi)發(fā)一個(gè)組件,這個(gè)組件需要對(duì)組件使用者提供一個(gè) ref 屬性,用于讓組件使用者獲取具體的 DOM 元素,我們就需要進(jìn)行 Refs 轉(zhuǎn)發(fā),這對(duì)于 class 組件并不是一個(gè)問(wèn)題,舉個(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 屬性不能通過(guò) this.props 獲取,所以這里換了一個(gè)屬性名 return <Child inputRef={this.inputRef} /> } }
但對(duì)于函數(shù)式組件,這卻是一個(gè)問(wèn)題。
我們是不能在函數(shù)組件上使用 ref 屬性的,因?yàn)楹瘮?shù)組件沒(méi)有實(shí)例。
所以 React 提供了 forwardRef 這個(gè) API,我們直接看使用示例:
// 3. 子組件通過(guò) forwardRef 獲取 ref,并通過(guò) 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} /> } }
尤其是在我們編寫(xiě)高階組件的時(shí)候,往往要實(shí)現(xiàn) refs 轉(zhuǎn)發(fā)。我們知道,一個(gè)高階組件,會(huì)接受一個(gè)組件,返回一個(gè)包裹后的新組件,從而實(shí)現(xiàn)某種功能的增強(qiáng)。
但也正是如此,我們添加 ref,獲取的會(huì)是包裹后的新組件的實(shí)例,而非被包裹的組件實(shí)例,這就可能會(huì)導(dǎo)致一些問(wèn)題。
createRef 源碼
現(xiàn)在我們看下 createRef
的源碼,源碼的位置在 /packages/react/src/ReactCreateRef.js
,代碼其實(shí)很簡(jiǎn)單,就只是返回了一個(gè)具有 current 屬性的對(duì)象:
// 簡(jiǎn)化后 export function createRef() { const refObject = { current: null, }; return refObject; }
在渲染的過(guò)程中,refObject.current
會(huì)被賦予具體的值。
forwardRef 源碼
那 forwardRef 源碼呢?源碼的位置在 /packages/react/src/ReactForwardRef.js
,代碼也很簡(jiǎn)單:
// 簡(jiǎn)化后 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 源碼解讀》,我們這里簡(jiǎn)單分析一下,以 InputComponent
為例:
// 使用 forwardRef const InputComponent = forwardRef(({value}, ref) => ( <input ref={ref} className="FancyButton" value={value} /> )); // 根據(jù) forwardRef 的源碼,最終返回的對(duì)象格式為: const InputComponent = { $$typeof: REACT_FORWARD_REF_TYPE, render, } // 使用組件 const result = <InputComponent /> // Bable 將其轉(zhuǎn)譯為: const result = React.createElement(InputComponent, null); // 最終返回的對(duì)象為: const result = { $$typeof: REACT_ELEMENT_TYPE, type: { $$typeof: REACT_FORWARD_REF_TYPE, render, } }
我們嘗試著打印一下最終返回的對(duì)象,確實(shí)也是這樣的結(jié)構(gòu):
React 系列
以上就是React 之 Refs 的使用和 forwardRef 的源碼解讀的詳細(xì)內(nèi)容,更多關(guān)于React Refs使用forwardRef 的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
在?React?中使用?Context?API?實(shí)現(xiàn)跨組件通信的方法
在React中,ContextAPI是一個(gè)很有用的特性,可用于組件間的狀態(tài)共享,它允許跨組件傳遞數(shù)據(jù)而無(wú)需通過(guò)每個(gè)組件手動(dòng)傳遞props,本文給大家介紹在?React?中如何使用?Context?API?來(lái)實(shí)現(xiàn)跨組件的通信,感興趣的朋友一起看看吧2024-09-09React.js?Gird?布局編寫(xiě)鍵盤(pán)組件
這篇文章主要介紹了React.js?Gird?布局編寫(xiě)鍵盤(pán)組件,Grid?布局則是將容器劃分成"行"和"列",產(chǎn)生單元格,然后指定"項(xiàng)目所在"的單元格,可以看作是二維布局2022-09-09淺談React組件props默認(rèn)值的設(shè)置
本文主要介紹了淺談React組件props默認(rèn)值的設(shè)置,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04React Hooks - useContetx和useReducer的使用實(shí)例詳解
這篇文章主要介紹了React Hooks - useContetx和useReducer的基本使用,本文通過(guò)實(shí)例代碼給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-11-11react如何利用useRef、forwardRef、useImperativeHandle獲取并處理dom
這篇文章主要介紹了react如何利用useRef、forwardRef、useImperativeHandle獲取并處理dom,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-10-10React實(shí)現(xiàn)歌詞滾動(dòng)效果(跟隨音樂(lè)播放時(shí)間滾動(dòng))
這篇文章主要為大家詳細(xì)介紹了React實(shí)現(xiàn)歌詞滾動(dòng)效果(跟隨音樂(lè)播放使勁按滾動(dòng)),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2024-02-02