iT邦幫忙

2022 iThome 鐵人賽

DAY 15
0

本文將提及以下內容

  • 做到什麼事情?
  • 解決什麼問題?
  • useRef代入參數
  • 在jsx的屬性當中加入ref
  • focus效果範例
  • ref無法藉由react管理
  • 簡述實際應用場景

做到什麼事情?

  • 當希望組件記得某些事情,但是又不想要觸發重新渲染的時候,我們可以使用useRef。
  • 選到某個native DOM實體

解決什麼問題?

有時候我們得要等到畫面渲染之後才進行Javascript的操控,例如在input的欄位,實現聚焦效果(focus),我們需要確切只到實際的DOM,因此這時候就得透過useRef用來存取實際DOM元素了。

根據react beta版本的官方文件解釋

useRef is a React Hook that lets you reference a value that’s not needed for rendering.

大致意思是useRef是讓你參照一個值卻不會發生重新渲染的hook。

useRef代入參數

我們可以嘗試著在useRef當中帶入"hello"字串

觀看以下範例

import { useRef } from 'react'
function App() {
  const helloRef = useRef("hello")
  console.log(helloRef);
  return (
    <div>
      App
    </div>
  )
}
export default App

最後呈現下圖

可以看到上述印出helloRef會印出一個物件,裡面有一個current的key,物件的值是"hello"。

這裡搭配使用useState、useEffect、useRef來記得渲染的次數,我們撰寫一下範例

import { useState, useRef, useEffect } from 'react'
function App() {
  const [text, setText] = useState("");
  const number = useRef(0);

  useEffect(() => {
      number.current = number.current + 1;
  });

  return (
    <>
      <input
          type="text"
          value={text}
          onChange={
          (e) => setText(e.target.value)
        }
      />
      <h1>總共渲染起次{number.current}</h1>
    </>
  );
}
export default App

由於useEffect的第二個值不帶入參數表示每次重新渲染後會執行useEffect裡面的事情,因此我們將number進行加總,不會因為重新渲染導致該current的值不一樣,換句話說,每次渲染的物件是同一個,因此我們在text輸入了a~j總共有十個英文字母就會渲染10次,如下圖

在jsx的屬性當中加入ref

我們可以透過useRef搭配屬性ref來存取DOM,換句話說,他提供了一個管道使我們可以參照到實際的HTMLelement。

function App() {
  const divRefContrainer = useRef(null);
  return (
    <div className="test" ref={divRefContrainer}>嗨</div>
  )
}

上面的語法相當於我們在使用vanilla javascript的下面的語法

const divElement = document.querySelector(".test")

focus效果範例

平時React將大多數的操作行為儲存在memory當中,等到先行比對Virtual Dom開始進行diff演算法,最後再實際掛載到真實的DOM當中,但有些事件是必須先渲染完後才能進行。

例如執行focus效果必須擁有真實的input DOM元素才能將效果呈現出來。我們可以觀看以下範例

import { useRef } from 'react'
function App() {
  const inputRef = useRef(null)
  const handClick = () => {
    inputRef.current.focus();
  }
  return (
    <>
      <input
          type="text"
          ref={inputRef}
      />
      <button onClick={handClick}>按我聚焦</button>
    </>
  );
}
export default App

ref無法藉由react管理

需要注意的地方,不要過度使用,大多數情形我們希望react來管理狀態,透過useRef將其掛載在DOM上面會導致state和資料不統一的情形,觀看以下範例

import { useRef, useState } from 'react'
function App() {
  const inputRef = useRef(null);
  const [inputValue, setInputValue] = useState("")
  const handClick = () => {
    inputRef.current.focus();
    inputRef.current.value = '為什麼被改變了'
  }
  console.log("沒有觸發rerender");
  return (
    <>
        <input
            type="text"
            ref={inputRef}
            value={inputValue}
            onChange={e => setInput(e.target.value)}
        />
        <div>我輸入的東西{inputValue}</div>
        <button onClick={handClick}>按我聚焦</button>
    </>
  );
}
export default App

通常在我們輸入字母到畫面上的input的時候應當會因為onChage事件,所以畫面會印出我所輸入的字母。

這時候如果我們改用按我聚焦的方式改變畫面上的input內容時卻不會顯示
如下圖

這是因為我們透過useRef的方式將value輸入到input的元素當中,也沒有觸發reRender,而且inputValue和 inputRef.current.value並不一致,所以最後就沒有顯示在畫面上了。

簡述實際應用場景

  • 管理focus聚焦效果
    • input元素點擊後聚焦事件
  • 文字選取
    • 選取文字後可能需要複製文字
  • 影片播放元素
    • 影片播放的我們希望按暫停、播放等等
  • 觸發動畫
    • 像是滑鼠滾輪到某個定點後觸發動畫
  • Infinite Scroll
    • 無限滾動畫面

小結

另外文章有錯誤的部分歡迎糾正,希望有幫助到大家,/images/emoticon/emoticon42.gif

參考資料

上一篇
siedEffect是什麼?—useEffect的4種情形、2個clearup範例、常見用途與注意事項
下一篇
從useRef理解forwardRef再解說useImperativeHandle—目的與用法
系列文
從Create到React—用來實作使用者介面的JavaScript函式庫30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言