在 React 自定义钩子中正确输入 useRef 值

Posted

技术标签:

【中文标题】在 React 自定义钩子中正确输入 useRef 值【英文标题】:Properly typing useRef values in a React custom hook 【发布时间】:2021-06-29 20:45:51 【问题描述】:

我有一个自定义钩子,它在我的 TS 应用程序中返回值 useRef。不幸的是,它抱怨我返回的 ref 的类型,我不知道如何正确输入。

这就是我的钩子的样子:


interface MyHookInterface 
    someBooleanState: boolean
    myRef: htmlElement | null


  
const useMyHook = (): MyHookInterface => 
    const [someBooleanState, setSomeBooleanState] = useState<boolean>(false)
    const myRef = useRef<HTMLElement | null>(null)
  
    useEffect(() => 
      const el = myRef.current // what type should this be?
      // Complaining that el possibly undefined 
      if(el?.offsetWidth < el?.scrollWidth)
          // do stuff
      
    , [])
  
    return 
        someBooleanState,
        myRef, // Getting: Type 'MutableRefObject<HTMLElement | null>' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, dir, and 234 more
    
  
  

正如您在 cmets 中看到的,我的钩子有一些与打字有关的错误:

1- 不知道如何在界面中输入myRef。请记住,它是用于多种类型的 HTML 元素,所以我不能在这里指定它是 div 还是什么。

2- 不知道如何输入el,但对其属性的访问抱怨它是undefined

如何在我的钩子中正确键入这些值?

【问题讨论】:

在您的界面中,您将 myRef 的返回类型指定为 HTMLELement,但在 useMyHook 中您将返回 Ref 对象。你应该返回 myRef: myRef.current 【参考方案1】:

这是因为 ref 的值存储在“.current”属性下。

const Comp = () => 
...
    const refVal = React.useRef(2);
    console.log(2 === refVal); // false
    console.log(2 === refVal.current); // true
...

解决方案取决于您的意图 - 如果您想返回 ref 本身,请按照类型错误的建议将接口的类​​型更改为 MutableRefObject&lt;HTMLElement | null&gt;,否则,将返回值替换为:

    return 
        someBooleanState,
        myRef: myRef.current,
    

如果 el 尚未定义(即尚未分配),您可能会收到 undefineds,因为您使用 ?. 运算符访问属性(需要明确的是,这是正确的。

例如

null?.test === undefined; // true

要解决这个问题,请检查 el 是否已定义且可选(尽管接口的定义并不要求这样做),检查两个值是否已定义且均为数字(即使用 el &amp;&amp; !isNaN(el?.offsetWidth) &amp;&amp; !isNaN(el?.scrollWidth) &amp;&amp; el.offsetWidth &lt; el.scrollWidth。或者,使用 nullish 合并运算符 if这适用于您的用例,即(el?.offsetWidth ?? 0) &lt; (el?.scrollWidth ?? 0)

【讨论】:

【参考方案2】:

ref 的类型不仅仅是它所引用的对象。这是一个React.MutableRefObject,它包含了它所引用的内容。这就是提供 current 属性的原因,因此 ref 可以工作。

如果您仔细考虑myRef,您应该会看到您需要的类型。在这种情况下:

React.MutableRefObject<HTMLElement | null>

这会让你的钩子返回如下类型:

interface MyHookInterface 
    someBooleanState: boolean
    myRef: React.MutableRefObject<HTMLElement | null>


其次,这个不起作用的原因:

  const el = myRef.current // what type should this be?
  
  // Complaining that el possibly undefined 
  if(el?.offsetWidth < el?.scrollWidth)
      // do stuff
  

是因为你的 ref 可能还没有被赋值。这意味着el?.offsetWidthundefined,因为el 仍然是null。而undefined 不是&lt; 比较中的有效操作数。 (if (undefined &lt; undefined) 没有多大意义)

您可以通过在进行比较之前检查以确保 el 存在来轻松解决此问题:

  if (el && el.offsetWidth < el.scrollWidth)
      // do stuff
  

Working example

【讨论】:

关于 Object possibly undefined 警告,我认为可选的链接运算符 (?.) 会解决这个问题。不过没关系,我可以做检查。否则,一切都好! 正如我在上面的答案中添加的那样,?。运算符是警告的原因,而不是解决方案 - 如果 el 为 null / undefined,它确实会返回 undefined - 即null?.test === undefined @theJuls ?. 是一个非常方便的操作符,但它并不总是适合所有情况。在这种情况下,它确实可以让您安全地访问可能是 null 对象的属性。但是,它不允许您在 undefined 上进行数学运算。

以上是关于在 React 自定义钩子中正确输入 useRef 值的主要内容,如果未能解决你的问题,请参考以下文章

React Hooks(钩子函数)

React 钩子 useRef() 如何在幕后工作?那个参考到底是啥?

nextjs 自定义响应检测钩子调用了两次

初始渲染时未定义 useRef 值

在自定义输入元素上使用带有打字稿的 useRef

[使用useref钩子将鼠标悬停在具有React的卡上