iT邦幫忙

2024 iThome 鐵人賽

DAY 8
0
Modern Web

前進React 生態系 : 技術應用與概念解析系列 第 8

Day 08 - 掌握 React 中的 Ref:useRef、Callback Ref 與 React 19 新特性介紹

  • 分享至 

  • xImage
  •  

useRef 的使用時機

  • 保存狀態:useRef 儲存的值不會因為元件重新渲染而改變,適合用來保存不需要觸發重新渲染的資料,像是表單輸入框的狀態、倒數計時器等。
  • 獲取 DOM 元素:useRef 最常見的用途是用來存取 DOM 元素的引用,方便操作該元素。
  • 搭配第三方套件:useRef 也常被用來將 DOM 元素的引用傳遞給第三方套件。

Callback Ref

除了一般常見的 useRef 定義方式,React 還提供了另一種方式叫 callback ref。callback ref 是將一個函數傳遞給 ref 屬性,在一些特殊情況很實用。

Callback Ref 的使用時機

處理 focus 或 scroll 相關事件

如果想自動讓某個元素 focus,一般常見的寫法如下:

import { useEffect, useRef } from "react";

export default function AutoFocusInput() {
  const inputRef = useRef<HTMLInputElement | null>(null);
  // 因為元素首次 render 還沒有 mounted,所以參數的預設值為 null。

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} type="text" />;
}

但如果根據條件渲染元素時,ref 會是 null,所以即使執行 useEffect 還是不會 focus 。

import { useState, useEffect, useRef } from "react";

export default function AutoFocusInput() {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const [showInput, setShowInput] = useState(false);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return (
    <div>
      <button onClick={() => setShowInput(true)}>Show Input</button>
      {showInput && <input ref={inputRef} type="text" />}
    </div>
  );
}

可以透過 callback ref 解決

import { useState, useCallback } from "react";

export default function AutoFocusInput() {
  const [showInput, setShowInput] = useState(false);
  const inputRef = useCallback((node: HTMLInputElement | null) => {
    node?.focus();
  }, []);

  return (
    <div>
      <button onClick={() => setShowInput(true)}>Show Input</button>
      {showInput && <input ref={inputRef} type="text" />}
    </div>
  );
}

透過 useCallback 避免每次渲染都重新建立 callback 函數。

當需要為不確定數量的清單項目分配 ref

可以參考 React 文件的這個例子

其他更多使用情境可以參考這篇文章

Cleanup functions for refs

在 React 19 有新增了 callback cleanup function,類似 useEffect,當元件 unmount 時執行。

<input
  ref={(ref) => {
    // ref created

    // NEW: return a cleanup function to reset
    // the ref when element is removed from DOM.
    return () => {
      // ref cleanup
    };
  }}
/>

若要使用 React 19 ,要先更新到最新版本。

npm install --save-exact react@rc react-dom@rc

forwardRef

forwardRef 是一個高階函數 (high-order function),允許將 ref 轉發到函數元件內部的某個子元素。這樣就能夠在函數元件中獲取到內部 DOM 元素的引用。

為什麼需要 forwardRef?

functional components 預設是不支援直接使用 ref 的。這是因為 functional components 本身並沒有實例(instance),不像 class components 有 this 來指向元件實例。因此如果你直接在 functional components 上使用 ref,會無法取得預期的結果並出現錯誤訊息。

實際範例

import { ComponentProps, forwardRef } from "react";

type ButtonProps = ComponentProps<"button"> & {
  icon?: React.ReactNode;
  text: string;
};

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ icon, text, ...props }, ref) => {
    return (
      <button
        ref={ref}
        className="flex items-center space-x-2 px-4 py-2 bg-blue-500 text-white rounded-md"
        {...props}
      >
        {icon && <span>{icon}</span>}
        <span>{text}</span>
      </button>
    );
  }
);

export default Button;

ref as a prop

在 React 19 後就可以不用用到 forwardRef 了,可以直接使用 ref 作為 props。

function CustomInput({ placeholder, ref }) {
  return <input placeholder={placeholder} ref={ref} />;
}

// ...

<CustomInput ref={ref} />;

參考資料:

https://tkdodo.eu/blog/avoiding-use-effect-with-callback-refs
https://react.dev/learn/manipulating-the-dom-with-refs#
https://julesblom.com/writing/ref-callback-use-cases
https://react.dev/blog/2024/04/25/react-19
https://react.dev/reference/react/forwardRef#
https://www.youtube.com/watch?v=m4QbeS9BTNU


上一篇
Day 07 - 結合 Zod 與 React Hook Form 實現簡潔的表單管理
下一篇
Day 09 - 掌握 Client State:React 狀態管理的核心概念
系列文
前進React 生態系 : 技術應用與概念解析30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言