網站首頁 編程語言 正文
三種使用方式
React 提供了 Refs,幫助我們訪問 DOM 節點或在 render 方法中創建的 React 元素。
React 提供了三種使用 Ref 的方式:
1. String Refs
class App extends React.Component { constructor(props) { super(props) } componentDidMount() { setTimeout(() => { // 2. 通過 this.refs.xxx 獲取 DOM 節點 this.refs.textInput.value = 'new value' }, 2000) } render() { // 1. ref 直接傳入一個字符串 return ( <div> <input ref="textInput" value='value' /> </div> ) } } root.render(<App />);
2. 回調 Refs
class App extends React.Component { constructor(props) { super(props) } componentDidMount() { setTimeout(() => { // 2. 通過實例屬性獲取 DOM 節點 this.textInput.value = 'new value' }, 2000) } render() { // 1. ref 傳入一個回調函數 // 該函數中接受 React 組件實例或 DOM 元素作為參數 // 我們通常會將其存儲到具體的實例屬性(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 創建 Refs // 并將 Refs 分配給實例屬性 textInputRef,以便在整個組件中引用 this.textInputRef = React.createRef(); } componentDidMount() { setTimeout(() => { // 3. 通過 Refs 的 current 屬性進行引用 this.textInputRef.current.value = 'new value' }, 2000) } render() { // 2. 通過 ref 屬性附加到 React 元素 return ( <div> <input ref={this.textInputRef} value='value' /> </div> ) } }
這是最被推薦使用的方式。
兩種使用目的
Refs 除了用于獲取具體的 DOM 節點外,也可以獲取 Class 組件的實例,當獲取到實例后,可以調用其中的方法,從而強制執行,比如動畫之類的效果。
我們舉一個獲取組件實例的例子:
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> ) } }
在這個例子中,我們通過 this.inputRef.current
獲取到 Input 組件的實例,并調用了實例的 handleFocus 方法,在這個方法中,又通過 Refs 獲取到具體的 DOM 元素,執行了 focus 原生方法。
Refs 轉發
有的時候,我們開發一個組件,這個組件需要對組件使用者提供一個 ref 屬性,用于讓組件使用者獲取具體的 DOM 元素,我們就需要進行 Refs 轉發,這對于 class 組件并不是一個問題,舉個示例代碼:
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. 創建 refs this.inputRef = React.createRef(); } componentDidMount() { setTimeout(() => { // 4. 使用 this.inputRef.current 獲取子組件中渲染的 DOM 節點 this.inputRef.current.value = 'new value' }, 2000) } render() { // 2. 因為 ref 屬性不能通過 this.props 獲取,所以這里換了一個屬性名 return <Child inputRef={this.inputRef} /> } }
但對于函數式組件,這卻是一個問題。
我們是不能在函數組件上使用 ref 屬性的,因為函數組件沒有實例。
所以 React 提供了 forwardRef 這個 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. 創建 refs this.inputRef = React.createRef(); } componentDidMount() { setTimeout(() => { // 4. 使用 this.inputRef.current 獲取子組件中渲染的 DOM 節點 this.inputRef.current.value = 'new value' }, 2000) } render() { // 2. 傳給子組件的 ref 屬性 return <Child ref={this.inputRef} /> } }
尤其是在我們編寫高階組件的時候,往往要實現 refs 轉發。我們知道,一個高階組件,會接受一個組件,返回一個包裹后的新組件,從而實現某種功能的增強。
但也正是如此,我們添加 ref,獲取的會是包裹后的新組件的實例,而非被包裹的組件實例,這就可能會導致一些問題。
createRef 源碼
現在我們看下 createRef
的源碼,源碼的位置在 /packages/react/src/ReactCreateRef.js
,代碼其實很簡單,就只是返回了一個具有 current 屬性的對象:
// 簡化后 export function createRef() { const refObject = { current: null, }; return refObject; }
在渲染的過程中,refObject.current
會被賦予具體的值。
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
,但最終創建的 React 元素的 $$typeof
依然為 REACT_ELEMENT_TYPE
。
關于 createElement
的源碼分析參考 《React 之 createElement 源碼解讀》,我們這里簡單分析一下,以 InputComponent
為例:
// 使用 forwardRef const InputComponent = forwardRef(({value}, ref) => ( <input ref={ref} className="FancyButton" value={value} /> )); // 根據 forwardRef 的源碼,最終返回的對象格式為: const InputComponent = { $$typeof: REACT_FORWARD_REF_TYPE, render, } // 使用組件 const result = <InputComponent /> // Bable 將其轉譯為: const result = React.createElement(InputComponent, null); // 最終返回的對象為: const result = { $$typeof: REACT_ELEMENT_TYPE, type: { $$typeof: REACT_FORWARD_REF_TYPE, render, } }
我們嘗試著打印一下最終返回的對象,確實也是這樣的結構:
React 系列
React 之 createElement 源碼解讀
React 之元素與組件的區別
原文鏈接:https://juejin.cn/post/7161719602652086308
相關推薦
- 2022-12-08 C#?如何調用C++?dll?string類型返回_C#教程
- 2023-11-26 StringBuffer 和 StringBuilder
- 2022-08-29 Python如何利用pandas讀取csv數據并繪圖_python
- 2023-01-31 基于C#實現獲取本地磁盤目錄_C#教程
- 2022-10-25 Git中smart?Checkout與force?checkout的區別及說明_相關技巧
- 2023-04-03 Python數據結構隊列解決約瑟夫斯問題_python
- 2022-07-23 C語言實例實現二叉搜索樹詳解_C 語言
- 2022-12-11 詳解Android?GLide圖片加載常用幾種方法_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支