为任何类型的 HTML 元素键入自定义 useRef 挂钩

Typing a custom useRef hook for any type of HTML element

我对 Typescript 还是有点陌生​​,我的目标是在 Typescript 中创建一个自定义 React.useRef 挂钩,returns 一个引用。我希望为任何 HTML 元素键入此 ref 而不仅仅是按钮。我 运行 遇到了正确输入此自定义挂钩的问题。

这是我的钩子:

type CustomRefHook = (dependencies?: string | boolean | number[]) => React.MutableRefObject<HTMLButtonElement | null>;

const useCustomRefHook: CustomRefHook = (...dependencies) => {
  const ref = React.useRef<HTMLButtonElement | null>(null);

  useEffect(() => {
    // ... do stuff
  }, dependencies)

  return ref;
}

目前,我对这个钩子的大部分用例都是按钮,但我想最终将其扩展到任何类型的 HTML 元素。我试过使用 HTMLElement 而不是 HTMLButtonElement ,但是我得到一个类型错误:

Type 'MutableRefObject<HTMLElement | null>' is not assignable to type 'string | ((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | null | undefined'.
  Type 'MutableRefObject<HTMLElement | null>' is not assignable to type 'RefObject<HTMLButtonElement>'.
    Types of property 'current' are incompatible.
      Type 'HTMLElement | null' is not assignable to type 'HTMLButtonElement | null'.

每当我尝试将此引用附加到按钮时。使用 HTMLButtonElement 消除 Typescript 错误,但我再次希望这个钩子能够处理任何类型的 HTML 元素。

ref 的类型需要与它所附加的 DOM 元素完全匹配,因此您需要使用泛型来指定元素类型。

由于 useCustomRefHook 现在是一个泛型函数,声明内联类型比声明为单独的类型更有意义 CustomRefHook.

我已允许此挂钩接受 Tany 值,但如果您只想将其应用于 HTML 元素,则可以使用 T extends HTMLElement。这可能很重要,具体取决于 useEffect 中“做事”的内容。如果“做事”需要关于 T 的任何特定内容,那么您需要确保 T 使用所需的 properties/methods.

扩展某些内容
import React, { MutableRefObject, DependencyList, useEffect, useRef } from "react";

const useCustomRefHook = <T extends any>( dependencies: DependencyList = [] ): MutableRefObject<T | null> => {

  const ref = useRef<T | null>(null);

  useEffect(() => {
    // ... do stuff
  }, dependencies);

  return ref;
};

挂钩 returns 一个基于通用 TMutableRefObject<T | null>T 的值不可能从参数中推断出来,因此每次调用它时都需要指定泛型。

const Test = () => {
  const ref = useCustomRefHook<HTMLButtonElement>();

  return <button ref={ref} />;
};