iT邦幫忙

2023 iThome 鐵人賽

DAY 12
0
Modern Web

react 學習記錄系列 第 12

[Day12]我的 react 學習記錄 - useRef

  • 分享至 

  • xImage
  •  

這篇文章的主要內容

簡單介紹 useRef hook 的使用方法


useRef

useRef 是 react 裡面除了 useState 以外另外一個用來儲存資料的 hook,也可以用來取得 DOM 節點。

Syntax

const ref = useRef(initialValue)

initialValue: 初始資料
ref: 一個只有 current 屬性的物件,而 current 的 value 會等於你放入的 initialValue

注意事項

  • hook 只能在元件裡使用,且只能在元件的最外層使用,不能放在迴圈或是判斷式裡,如果有需要可以建立一個新的子元件,放在子元件裡。
  • 可透過 mutate 的方式修改裡面的 value,e.g., ref.current = somevalue

儲存資料

可以把 useRef 當做一個全域的變數用儲存資料,但是這個變數被改變時並不會觸發 re-render,而且即使元件 re-render 了也還是可以把資料保留下來。

import { useRef } from "react";
function App() {
  const [count, setCount] = useState<number>(0);
  const numberRef = useRef<number>(0);

  function addCount() {
    setCount(count + 1);
  }

  function addNumber() {
    numberRef.current += 1;
    console.log("numberRef", numberRef.current);
  }

  console.log("component render");
  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={addCount}>add Count</button>
      <button onClick={addNumber}>add Number</button>
    </div>
  );
}

ref1

可以看到即使 re-render,numberRef.current 的資料還是被保留下來了,而且我是在修改了 numberRef.current 之後就馬上 console,value 也是馬上就被改變了,不像 setState 的改變是非同步的。

其實 useRef 就是一個單純的 JavaScript 物件。


取得 DOM 元素

前面幾篇文章都有提到,當我們使用 react 進行開發時會盡量的減少對 DOM 的操作,讓 react 幫我們處理,但是總有非用不可的時候,這個時候就可以透過 useRef 來幫助我們,useRef 也可以用來取得 DOM 元素。

import { useRef } from "react";
import dog from "./assets/dog.mp4";
function App() {
  const videoRef = useRef<HTMLVideoElement>(null);

  // 處理影片應該播放 / 停止
  function togglePlayVideo() {
    if (!videoRef.current) return;

    if (videoRef.current.paused) {
      videoRef.current.play();
    } else {
      videoRef.current.pause();
    }
  }

  // 快進 5 秒
  function handleFastForward() {
    if (!videoRef.current) return;
    videoRef.current.currentTime += 5;
  }

  // 倒退 5 秒
  function handleRewind() {
    if (!videoRef.current) return;
    videoRef.current.currentTime -= 5;
  }

  return (
    <div>
      <h1>useRef</h1>
      <video ref={videoRef} src={dog} width="350" controls />
      <br />
      <button onClick={handleRewind}>倒退 5 秒</button>
      <button onClick={togglePlayVideo}>暫停/播放</button>
      <button onClick={handleFastForward}>快轉 5 秒</button>
    </div>
  );
}

每一個 JSX 的 html 標籤都有一個 ref 屬性,value 就是我們用 useRef hook 取得的 ref 物件。

以上面的例子,可以透過 videoRef.current 來取得 ref 放置的 DOM 元素,這裡使用 <video> 來示範,可以做到播放或是修改播放時間來實現快轉、倒退跟暫停的功能。

ref2


useRef 裡面儲存多個元素

另外還有一個情形假設有一個影片列表,但是希望可以各別操作,一開始想到的可能會是這樣。

const videoRefOne = useRef<HTMLVideoElement>(null);
const videoRefTwo = useRef<HTMLVideoElement>(null);

一、兩個可能還好,但是如果有十幾個怎麼辦,取得 ref 還有另一個寫法。

ref={(ele) => ele ...}

import { useRef } from "react";
import dog from "./assets/dog.mp4";
const videoList = [
  { videoUrl: dog, id: "1" },
  { videoUrl: dog, id: "2" },
];

function App() {
  const videoRefList = useRef<HTMLVideoElement[]>([]);

  // 處理影片應該播放 / 停止
  function togglePlayVideo(index: number) {
    if (!videoRefList.current[index]) return;
    if (videoRefList.current[index].paused) {
      videoRefList.current[index].play();
    } else {
      videoRefList.current[index].pause();
    }
  }

  // 快進 5 秒
  function handleFastForward(index: number) {
    if (!videoRefList.current) return;
    videoRefList.current[index].currentTime += 5;
  }

  // 倒退 5 秒
  function handleRewind(index: number) {
    if (!videoRefList.current) return;
    videoRefList.current[index].currentTime -= 5;
  }

  return (
    <div>
      <h1>useRef</h1>
      {videoList.map(({ videoUrl, id }, index) => (
        <div key={id} style={{ display: "inline-block", margin: "5px" }}>
          <video
            ref={(ele) => ele && (videoRefList.current[index] = ele)}
            src={videoUrl}
            width="350"
            controls
          />
          <br />
          <button onClick={() => handleRewind(index)}>倒退 5 秒</button>
          <button onClick={() => togglePlayVideo(index)}>暫停/播放</button>
          <button onClick={() => handleFastForward(index)}>快轉 5 秒</button>
        </div>
      ))}
    </div>
  );
}

ref 的屬性可以接收一個 function 作為 value,當使用 function 作為 value 時,那個 function 可以接收到元素本身,可以透過 (videoRefList.current[index] = ele) 這個寫法讓一個 useRef 同時儲存多個元素。

ref3


useRef - react document

下一篇簡單介紹 react forwardRef 跟 useImperativeHandle
如果內容有誤再麻煩大家指教,我會盡快修改。

這個系列的文章會同步更新在我個人的 Medium,歡迎大家來看看 👋👋👋
Medium


上一篇
[Day11]我的 react 學習記錄 - useEffect
下一篇
[Day13]我的 react 學習記錄 - react forwardRef & useImperativeHandle
系列文
react 學習記錄30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言