【问题标题】:In React, using TypeScript, how do I pass a ref to a custom component using RefForwardingComponent and forwardRef?在 React 中,使用 TypeScript,如何使用 RefForwardingComponent 和 forwardRef 将 ref 传递给自定义组件?
【发布时间】:2020-05-19 23:45:03
【问题描述】:

我正在尝试将 ref 传递给自定义组件(以便我可以将焦点设置到该组件)。

但我不断收到此错误

const RefComp: React.RefForwardingComponent<HTMLInputElement, Props>
Type '{ value: string; onChange: Dispatch<SetStateAction<string>>; ref: MutableRefObject<HTMLInputElement | undefined>; }'
is not assignable to type 'IntrinsicAttributes & Props & { children?: ReactNode; }'.
  Property 'ref' does not exist on type 'IntrinsicAttributes & Props & { children?: ReactNode; }'.ts(2322)

这是我的代码

import * as React from 'react';
import {useRef, useState, RefForwardingComponent, forwardRef} from 'react';

interface Props {
    value: string;
    onChange(event: string): void;
}

const RefComp: RefForwardingComponent<HTMLInputElement, Props> = forwardRef<
    HTMLInputElement,
    Props
>(({value, onChange}, ref) => (
    <input
        value={value}
        onChange={event => onChange(event.target.value)}
        ref={ref}
    />
));

export default function App() {
    const [val, setVal] = useState('');
    const inputRef = useRef<HTMLInputElement>();

    return (
        <div className="App">
            <RefComp value={val} onChange={setVal} ref={inputRef} />
            <p>{val}</p>
        </div>
    );
}

这是一个代码框 https://codesandbox.io/s/crimson-cdn-klfp5

如果我忽略错误,代码似乎可以正常工作。所以不知道为什么我会得到它......

谁能解释我为什么会收到这个错误,以及我如何解决它? :)

编辑:

如 cmets 中所述,您可以通过将 ref 添加到您的道具来解决此问题。

import * as React from 'react';
import {
    useRef,
    useState,
    RefForwardingComponent,
    forwardRef,
    MutableRefObject,
} from 'react';

interface Props {
    value: string;
    onChange(event: string): void;
    ref?: MutableRefObject<HTMLInputElement | null>;
}

const RefComp: RefForwardingComponent<HTMLInputElement, Props> = forwardRef<
    HTMLInputElement,
    Props
>(({value, onChange}, ref) => (
    <input
        value={value}
        onChange={event => onChange(event.target.value)}
        ref={ref}
    />
));

export default function App() {
    const [val, setVal] = useState('');
    const inputRef = useRef<HTMLInputElement>(null);

    return (
        <div className="App">
            <RefComp value={val} onChange={setVal} ref={inputRef} />
            <p>{val}</p>
        </div>
    );
}

但这感觉不对。现在我们说 ref 既作为 props 的一部分,又作为传递给 forwardRef 的函数的第二个回调参数

如果我这样写,我的问题可能会更清楚

const RefComp: RefForwardingComponent<HTMLInputElement, Props> = forwardRef<
    HTMLInputElement,
    Props
>((props: Props, ref) => (
    <input
        value={props.value}
        onChange={event => props.onChange(event.target.value)}
        ref={ref /* here, props.ref is also available, but wouldn't work */ }
    />
));

编辑 2:

这是一个相关的问题

Typescript RefForwardingComponent not working

编辑 3:

我找到了相关的源代码。 https://github.com/DefinitelyTyped/DefinitelyTyped/blob/33f6179e0f25b0ca798ad89a667c0a27ea0c98dd/types/react/index.d.ts#L702

function forwardRef<T, P = {}>(Component: RefForwardingComponent<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;

所以forwardRef 函数返回一个ForwardRefExoticComponent&lt;PropsWithoutRef&lt;P&gt; &amp; RefAttributes&lt;T&gt;&gt; 类型的组件。这是一个组件,它采用与RefAttributes&lt;T&gt; 相交的PropsWithoutRef&lt;P&gt; 类型的道具。这两种类型也在同一个文件中定义。

据我了解,第一个基本上只是从 P 中排除 ref(在我的原始示例中是 Props)。第二个定义为

interface RefAttributes<T> extends Attributes {
    ref?: Ref<T>;
}

鉴于这些信息,我真的不明白为什么我必须将ref 添加到我自己的Props 中。看起来forwardRef 真的应该为我做这件事——甚至实际上从我自己的Props 中删除ref...

有人可以解释一下这里发生了什么吗?

【问题讨论】:

  • 直接将 ref 声明添加到interface Props 似乎效果很好
  • @keikai 我也注意到了,为什么需要它?

标签: reactjs typescript react-hooks


【解决方案1】:

问题是RefComp 的类型定义错误。删除它(让 TypeScript 推断类型),代码就可以正常工作了。

import * as React from 'react';
import {useRef, useState, forwardRef} from 'react';

interface Props {
    value: string;
    onChange(event: string): void;
}

const RefComp = forwardRef<HTMLInputElement, Props>(
    ({value, onChange}, ref) => (
        <input
            value={value}
            onChange={event => onChange(event.target.value)}
            ref={ref}
        />
    ),
);

export default function App() {
    const [val, setVal] = useState('');
    const inputRef = useRef<HTMLInputElement>(null);

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

    return (
        <div className="App">
            <RefComp value={val} onChange={setVal} ref={inputRef} />
            <p>{val}</p>
        </div>
    );
}

https://codesandbox.io/s/quizzical-liskov-bsruh

【讨论】:

    猜你喜欢
    • 2021-07-10
    • 1970-01-01
    • 2020-12-11
    • 2021-09-11
    • 1970-01-01
    • 2022-11-17
    • 2018-09-21
    • 2020-10-10
    • 2017-08-16
    相关资源
    最近更新 更多