React自定義Hook-useForkRef的具體使用

開篇

使用過 React 技術棧的同學相信都使用過 ref 傳遞給 render 中的元素,而在使用 React 封裝組件時,會有這樣一個場景:

組件將 props.children 作為 render 內容;組件內部會創建 ref 綁定到 props.children 上。

我們知道,元素上隻能綁定一個 ref 屬性引用,但對於上面這個場景,props.children 上可能已經存在一個 ref 屬性,而組件內部定義的 ref 也會綁定到 props.children 上。
我們要想一種方式,將兩者的 ref 都可以生效於元素上。

思路

首先我們回顧一下 React 創建 ref 的方式:

  • React.createRef():React 16.3 版本提供的 class 創建 ref 方式;
  • React.useRef():React Hooks 提供的 函數組件 創建 ref 方式;
  • 回調 Refs:傳遞一個函數作為元素的 ref 屬性,此函數接收 React 組件實例或 HTML DOM 元素作為參數。

綜合考慮,既然 回調 Refs 允許我們傳遞一個函數,並且接收元素實例作為這個函數的參數,那我們就可以定義一個這樣的函數,在函數內編寫我們的邏輯來處理 多個 ref 綁定元素實例的場景。(函數的靈活性)

實現

  • 編寫一個函數(閉包函數),接收 props.children.ref 和 組件內 ref 作為參數;
  • 函數(閉包函數)需要 return 返回一個函數,這個函數將作為 回調 Refs 去作用於元素;
  • 在 return 的這個函數中,將函數參數(元素引用)綁定到 props.children.ref 和 組件內 ref 上。

上代碼:

function forkRef(refA, refB) {
  return refValue => {
    setRef(refA, refValue);
    setRef(refB, refValue);
  };
}

function setRef(ref, value) {
  if (typeof ref === 'function') {
    ref(value);
  } else if (ref) {
    ref.current = value;
  }
}

在 setRef 中會針對創建 ref 的方式做不同處理,比如:React.createRef 和 React.useRef 創建的 ref 是一個具有 current 屬性的對象。

使用:

const nodeRef = React.useRef(null); // 組件內部的 ref

const handleRef = forkRef(props.children.ref, nodeRef);

const childrenProps = { ref: handleRef };

return React.cloneElement(children, childrenProps);

自定義 Hook – useForkRef

在 Hook 函數組件中,我們可以借助於 React.memo() 優化一下 forkRef() 的邏輯,避免每次組件更新時都創建一個新的閉包函數。
下面我們使用 TS 編寫一個 useForkRef:

import * as React from 'react';

interface MutableRefObject<T> {
  current: T;
}

type Ref<T> = ((instance: T | null) => void) | MutableRefObject<T> | null;

export function setRef(ref: Ref<unknown>, value: unknown) {
  if (typeof ref === 'function') {
    ref(value);
  } else if (ref) {
    ref.current = value;
  }
}

export default function useForkRef(refA: Ref<unknown>, refB: Ref<unknown>) {
  return React.useMemo(() => {
    if (refA == null && refB == null) {
      return null;
    }
    return (refValue: unknown) => {
      setRef(refA, refValue);
      setRef(refB, refValue);
    };
  }, [refA, refB]);
}

使用:

const nodeRef = React.useRef<HTMLElement>(null); // 組件內部的 ref
const handleRef = useForkRef(children.ref, nodeRef);

const childrenProps: any = { ref: handleRef };
React.cloneElement(children, childrenProps)

到此這篇關於React自定義Hook-useForkRef的具體使用的文章就介紹到這瞭,更多相關React Hook-useForkRe內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: