useRef TypeScript - 不可分配给 LegacyRef 类型<HTMLDivElement>

问题描述 投票:0回答:11

我正在尝试将

useRef
与 TypeScript 一起使用,但遇到了一些问题。

用我的

RefObject
(我假设)我需要访问
current
。 (即
node.current

我尝试过以下方法

  • const node: RefObject<HTMLElement> = useRef(null);
  • const node = useRef<HTMLElement | null>(null);

但是当我去设置

ref
时,我总是被告知 X
is not assignable to type 'LegacyRef<HTMLDivElement> | undefined'.

return <div ref={ node }>{ children }</div>

编辑:这不应限于任何一种类型的元素,因此不仅仅是

HTMLDivElement | HTMLFormElement | HTMLInputElement

编辑:这应该作为示例

import React, { useRef, RefObject } from 'react';

function Test() 
{
    // const node = useRef(null);
    // const node: RefObject<HTMLElement> = useRef(null);
    const node = useRef<HTMLElement | null>(null);

    if (
        node &&
        node.current &&
        node.current.contains()
    ){ console.log("current accessed")}

    return <div ref={ node }></div>
}
reactjs typescript tsx
11个回答
263
投票

以上方法都不适合我,但事实证明解决方案非常简单......

我做错的只是没有在 useRef 初始化中明确包含“null”作为参数(它需要 null,而不是未定义)。 另外,您不能使用“HTMLElement”作为您的引用类型,您必须更具体,所以对我来说它是“HTMLDivElement”)。

所以对我来说工作代码是这样的:

const ref = useRef<HTMLDivElement>(null);

return <div ref={ref}> Some Content... </div>

67
投票

只需导入 React:

import React, { useRef } from 'react';

function Test() {
    const node = useRef<HTMLDivElement>(null);

    if (
        node &&
        node.current &&
        node.current.contains()
    ){ console.log("current accessed")}

    return <div ref={node}></div>
}

我更新了。使用

HTMLDivElement
作为通用参数而不是
HTMLElement | null
。另外,
contains
期待一个争论。

更新

useRef
需要 DOM 元素类型的通用参数。您不需要使用
| null
,因为
RefObject
已经知道
current
可能为空。

参见下一个类型:

interface RefObject<T> {
  readonly current: T | null
}

TS 和 React 足够聪明,可以发现你的 ref 可能为空


6
投票

同样代表

<svg>
元素:

const ref = useRef<SVGSVGElement>(null)
...
<svg ref={ref} />

5
投票

我来这里寻求 iframe 参考的帮助。也许这个解决方案会帮助其他正在寻找同样东西的人。我用

HTMLDivElement
替换了
HTMLIFrameElement
所以:

const node = useRef<HTMLIFrameElement>(null);

4
投票
selfref = React.createRef<HTMLInputElement>()

我给出了准确的TS Type,然后编辑器通过了检查。现在效果很好。


4
投票

作为解决方法,您可以显式强制转换:

const ref = useRef<HTMLDivElement>();
...
<input
      ref={ref as RefObject<HTMLInputElement>}
      ...
/>

2
投票

查看这里的所有答案后,我不确定他们中的任何一个是否完整地回答/解释了问题。在这种情况下,问题有两个:

  • 我们需要使用不同的 HTML 界面对象 -> HTMLDivElement
  • 我们需要将默认值传递给引用初始化

React useRef 函数实际上有 3 个不同的签名(在 React 16.8 中):

如果传递的默认值与泛型类型相同

function useRef\<T>(initialValue: T): MutableRefObject<T>;

如果我们传递与泛型类型相同类型的默认值或 null(如此处许多响应所发现的)

function useRef\<T>(initialValue: T|null): RefObject<T>;

如果我们不传递默认值和/或我们不传递泛型类型

function useRef\<T = undefined>(): MutableRefObject<T | undefined>;

真正的问题归结为 MutableRefObjectLegacyRef 的不兼容。我猜测 React 团队对引用的数据类型进行了更改,因此我们根据我们传递的内容拥有多个签名和引用类型。

如果能从 React eslint 获得更好的错误消息那就太好了,至少因为我们收到的通用 TS 错误消息不足以立即发现问题所在。

Tl;dr:将 null 作为 useRef 对象中的默认值传递给我们返回同时满足 MutableRefObjectLegacyRef (可能)

的引用对象

2
投票

我有同样的错误,有人建议先转换为未知:

ref={inputRef as unknown as RefObject<HTMLTextAreaElement>}

1
投票

关键是使用HTMLElement和undefined进行初始化

const node = useRef<HTMLElement>();

0
投票

问题原因

这里问题的根源不是你的 refObject 的类型(从

useRef()
返回的内容)。问题是
ref
prop
需要更具体的类型。

Div 的引用说:<​div ref={..🗣"Give me HTMLDivElement!”
画布的参考:<​canvas ref={..🗣"Give me HTMLCanvasElement!”`

例如,

<canvas>
的道具就像

// props of <canvas>
{
    ....
    ref: {
        current: HTMLCanvasElement
    },
};

如您所见,它期望获得

HTMLCanvasElement
。 如果我们尝试传递父类(
HTMLElement
),它会抱怨:“这对我来说还不够!它没有这样那样的属性!”

情况与此作业类似:

    // ❌ Error, of course
    const myElement: HTMLCanvasElement = new HTMLElement();

    // (Type 'HTMLElement' is missing the following properties from type 'HTMLCanvasElement': captureStream, getContext, .....)

缺乏解决方案

如果变量希望获得特定的值,您可以将什么“通用”值传递给变量?
— 无。

您甚至无法通过显式列出可能的选项来扩展其类型,例如

HTMLDivElement | HTMLFormElement | HTMLInputElement

const generalizedValue = getElement() as `HTMLDivElement | HTMLFormElement | HTMLInputElement`

// ❌ Nope! What if the passed object won't have "autofocus" or "placeholder" props ?!
const myCanvas: HTMLCanvasElement = generalizedValue;

// ❌ Nope! What if the passed object won't be able to captureStream() ?!
const myInput: HTMLInputElement = generalizedValue;

仍然是一个解决方案! (某种意义上)

您想让您的 ref 通用 - 所以它可能是在某些可重用的自定义挂钩中创建的。请毫不犹豫地在其中指定自定义泛型!

这就是父类派上用场的地方! —

<T extends HTMLElement>

const useMyCoolHook = <ElType extends HTMLElement>() => {
    const elRef = useRef<ElType>(null);

    //...your logic goes here...

    return elRef;
}

function MyButton() 
{
    const firstButtonRef = useMyCoolHook<HTMLButtonElement>(null); //<-- for <button>
    const anotherButtonRef = useMyCoolHook<HTMLButtonElement>(null); //<-- for <button>
    const linkRef = useMyCoolHook<HTMLAnchorElement>(null);   //<-- for <a>
    return (
        <>
            <button ref={ firstButtonRef } />
            <button ref={ anotherButtonRef } />
            <a ref={ linkRef } />
        </>
    );
}

如果在大多数情况下它将与一种特定类型的元素一起使用(例如,与

<div>
s),您可以将其设为通用的默认值:
<ElType extends HTMLElement = HTMLDivElement>

const useMyCoolHook = <ElType extends HTMLElement = HTMLDivElement>() => {
    ....

-1
投票

您在打字稿中收到此错误的原因是因为您需要输入 ref.current 而不仅仅是 ref

示例:

const ref = React.useRef<HTMLDivElement>(null);

Component(props: {ref?: {current: HTMLDivElement}})
© www.soinside.com 2019 - 2024. All rights reserved.