在 React 組件中如何使用 Refs

英文:Yomi Eluwande 譯文:joking_zhang

使用 React 時,我們的默認思維方式應該是 不會強制修改 DOM ,而是通過傳入 props 重新渲染組件。但是,有些情況卻無法避免修改 DOM 。

React 中的 Refs 提供了一種訪問 render() 方法中創建的 React 元素(或 DOM 節點)的方法。

在 React 組件中如何使用 Refs

當父組件需要與子組件交互時,我們通常使用 props 來傳遞相關信息。 但是,在某些情況下,我們可能需要修改子項,而不用新的props 重新呈現 (re-rendering) 它。 這時候就需要 refs 出場了。

我什麼時候應該使用 Refs ?

我們建議在以下情況下使用 refs:

  • 與第三方 DOM 庫集成
  • 觸發命令式動畫
  • 管理焦點,文本選擇或媒體播放

譯註:第三點是否也可以理解為使用 event 對象呢?在 React 中就是合成事件(SyntheticEvent)。

官方文檔中提到:避免使用 refs 來做任何可以通過聲明式實現來完成的事情。

所以一旦我們確定我們真的應該使用 refs,我們需要如何使用它們呢?

在 React 中使用 Refs

您可以通過多種方式使用 refs :

  • React.createRef()
  • 回調引用 (Callback refs)
  • String refs(已過時)
  • 轉發 refs (Forwarding refs)

接下來,讓我們看看每一種實現方式:

React.createRef()

可以使用該 React.createRef() 函數創建 Refs ,並通過該 ref 屬性附加到 React 組件中的 HTML 元素。

通常在組件的構造函數內創建 ref ,使其在整個組件中可用。例如:

class MyComponent extends React.Component {
 constructor(props) {
 super(props);
 this.firstRef = React.createRef();
 }
 render() {
 return 

; }}

如上所示:

  • 一個 ref 實例在構造函數中創建,並賦值給 this.firstRef
  • 在 render() 方法內部,將構造函數中創建的 ref 傳遞給 div

接下來,讓我們看一個在 React 組件中使用 refs 的示例。

在 React 組件中如何使用 Refs

使用 Refs 聚焦輸入

這是另一個例子:

// Ref.jsclass 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  ref
 // with the `textInput` that we created in the constructor
 return (
 

); }}

在上面的代碼塊中,我們構建了一個按鈕,當單擊它時,該頁面會自動聚焦在輸入框上。

在 React 組件中如何使用 Refs

首先,我們在構造方法中創建一個 ref 實例,並將其賦值給 this.textInput,然後通過 ref 屬性將其分配給 input 元素。

注意,當 ref 屬性被一個 HTML 元素使用時(比如當前示例中的 input 元素),在 constructor 中使用 React.createRef() 創建的 ref 會接收來自底層 DOM 元素的 current 值。

譯註:這裡的 current 應該是 合成事件(SyntheticEvent)

這意味著訪問 DOM 值,我們需要寫這樣的東西:

this.textInput.current;

第二個元素是一個按鈕,點擊它之後會自動聚焦到第一個輸入框上面。我們為 onClick 屬性設置了 this.focusTextInput 函數。

函數 focusTextInput() 使用了 JavaScript 構建 DOM 的標準函數。 .focus() 方法會將光標聚焦於文本輸入框上。

focusTextInput() {
 this.textInput.current.focus();}

最後,focusTextInput 函數綁定在這樣的 constructor 方法中的:

this.focusTextInput = this.focusTextInput.bind(this);

從 ref 中獲取值

在這個例子中,我們將看到如何為 input 輸入框設置 ref 屬性,並通過 ref 來獲取值。示例如下:

在這個例子中,我們創建了一個 input 輸入框來輸入值。然後,當單擊提交按鈕時,我們將讀取此值,並在控制檯打印。

// Ref.jsclass CustomTextInput extends React.Component {
 constructor(props) {
 super(props);
 // create a ref to store the textInput DOM element
 this.textInput = React.createRef();
 }
 handleSubmit = e => {
 e.preventDefault();
 console.log(this.textInput.current.value);
 };
 render() {
 // tell React that we want to associate the  ref
 // with the `textInput` that we created in the constructor
 return (
 

this.handleSubmit(e)}>

); }}

同樣,我們使用該 React.createRef() 函數創建一個 ref 實例,然後將它分配給實例變量 this.textInput。

在 render 函數中,我們希望讀取 form 下輸入框的值。我們如何讀取這個值? 通過為 input 指定一個 ref ,然後讀取 ref 的值。

點擊提交按鈕,上面示例中 form 元素會通過 onSubmit 方法,調用 this.handleSubmit 函數 ,並在控制檯打印輸入框中的信息。

handleSubmit = e => {
 e.preventDefault();
 console.log(this.textInput);};

上面,參數 e 包含事件對象。我們使用e.preventDefault() 來告訴瀏覽器我們正在處理被點擊的提交按鈕,我們不希望這個事件“冒泡”(意思就是說,阻止瀏覽器的默認行為)。

譯註:這裡可以看一下 React 對於事件的處理:在 React 中另一個不同點是你不能通過返回 false 的方式阻止默認行為。你必須顯式的使用 preventDefault

在上面示例中,我們打印了 this.textInput ,在控制檯可以看到一個 ref 對象。

> Object {current: HTMLInputElement}

請注意,它有一個 current屬性,即 HTMLInputElement 。這是 input DOM 元素本身,而不是實際值。 我們必須使用 this.textInput.current.value 來獲取 input 標籤的實際值:

handleSubmit = e => {
 e.preventDefault();
 console.log(this.textInput.current.value);};

使用 refs 是一種從表單中直接提取值的方式:只需要給 input 標籤設置 ref ,並在你需要的時候將值提取出來。

在 React 組件中如何使用 Refs

Refs 回調

Refs 回調 是在 React 中使用 ref 的另一種方式。要以這種方式使用 ref,我們需要為 ref 屬性設置回調函數。當我們設置 ref 時,React 會調用這個函數,並將 element 作為第一個參數傳遞給它。

這是另一個例子的代碼。像上面的示例一樣,此代碼獲取 input 標籤的文本值,但在這裡我們使用回調引用:

// Refs.jsclass CustomTextInput extends React.Component {
 constructor(props) {
 super(props);
 this.textInput = null;
 this.setTextInputRef = element => {
 this.textInput = element;
 };
 }
 handleSubmit = e => {
 e.preventDefault();
 console.log(this.textInput.value);
 };
 render() {
 return (
 

this.handleSubmit(e)}>

); }}

上面的示例中,我們將 input 標籤的 ref 設置為 this.setTextInputRef。

當組件安裝時,React 會將 DOM 元素傳遞給 ref 的回調;當組件卸載時,則會傳遞 null。(ref 回調會在 componentDidMount 和 componentDidUpdate 生命週期之前調用。)

在 React 組件中如何使用 Refs

String Ref(已過時)

還有另一種設置 refs 的方法,但它被認為是過時的,可能很快就會被棄用。但是你可能會在其他人的代碼中看到它,所以這裡說一下。

使用 string refs,你將會看到這樣的 input 標籤:

然後,我們可以在組建上得到這樣的值:this.refs.textInput.value - 但是,再次聲明,這不應該在新代碼中使用,因為這個 API 將被棄用。

轉發 Refs (Forwarding Refs)

Ref forwarding 是一種將 ref 通過組件傳遞給其子節點的技術。它對於可複用組件庫和高階組件(HOC)等情況非常有用。

您可以使用 React.forwardRef 函數將 ref 轉發到組件。我們來看下面的例子:

// Ref.jsconst TextInput = React.forwardRef((props, ref) => (
 ));const inputRef = React.createRef();class CustomTextInput extends React.Component {
 handleSubmit = e => {
 e.preventDefault();
 console.log(inputRef.current.value);
 };
 render() {
 return (
 

this.handleSubmit(e)}>

); }}

Ref forwarding 允許組件接收一個 ref ,並將它向下傳遞(換句話說,“轉發”它)給子組件。

在上面的示例中,我們使用 input 標籤創建了一個名為 TextInput 的組件。那麼,我們如何將 ref 傳遞或轉發到 input 標籤呢?

在 React 組件中如何使用 Refs

首先,我們使用下面的代碼創建一個 ref :

const inputRef = React.createRef();

然後,我們將 ref 通過為組件 指定一個同名的 JSX 的屬性,將 ref 向下傳遞。然後 React 將會把 ref 作為第二個參數轉發給 forwardRef 函數。

接下來,我們將此 ref 參數轉發給 。現在可以在外層組件通過 inputRef.current 訪問DOM節點的值了。

轉發 refs 和高階組件

最後,讓我們看一下使用 refs 的另一個例子,但這次是使用高階組件(HOC)。

在上面的示例應用程序中,會將所有 input 標籤中輸入的值在控制檯打印。這裡已經為 input 標籤設置了 ref 屬性,接下來,讓我們看一下需要如何在高階組件中傳遞 / 轉發 ref 。

const Input = InputComponent => {
 const forwardRef = (props, ref) => {
 const onType = () => console.log(ref.current.value);
 return ;
 };
 return React.forwardRef(forwardRef);};

這裡有一個名為 Input 的高階組件 ,它接受 InputComponent 作為參數。當用戶輸入的時候,他還會將 ref 的值在控制檯打印。

在 Input 高階組件內,forwardRef 函數會返回 InputComponent。forwardRef 函數中所包含的 ref 參數,是由 React.forwardRef 函數創建的。 高階組件最終會將包裝好的組件作為值返回。

在 React 組件中如何使用 Refs

接下來,我們創建一個組件,將 input 作為子組件包含進來。

const TextInput = ({ forwardedRef, children, ...rest }) => (
 

{children}

);

上面的組件會將 forwardedRef 分配給 ref 屬性, 當渲染子組件的時候,input 輸入框就會接收到這個 ref 。…rest 是 props 的解構(也就是說,我們會將 rest 數組中的所有參數作為 props 傳遞給 input 組件)。那麼我們該如何使用 TextInput 組件呢?像這樣:

const InputField = Input(TextInput);class CustomTextInput extends Component {
 render() {
 const inputRef = React.createRef();
 return ;
 }}

最後,將 TextInput 傳入 Input 高階組件,會返回一個 InputField component。

創建一個 ref ,並作為參數傳遞給 InputField 組件。

在 React 組件中如何使用 Refs

結論

與通過 props 和 state 不同,Refs 是一種將數據傳遞給特定子實例的好方法。

你必須要小心,因為 refs 操縱實際的 DOM,而不是虛擬的 DOM,這與 React 思維方式相矛盾。因此,雖然 refs 不應該是通過應用程序流動數據的默認方法,但是當您需要時,它們是可以從 DOM 元素讀取數據的好方法。

今天就到這裡啦,喜歡編程的小夥伴可以關注我哦!有學習方面的問題可以私信回覆:學習!


分享到:


相關文章: