首页 > 解决方案 > 在 React 中,使用 TypeScript,如何使用 RefForwardingComponent 和 forwardRef 将 ref 传递给自定义组件?

问题描述

我正在尝试将 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

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

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

编辑:

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:

这是一个相关的问题

打字稿RefForwardingComponent不起作用

编辑 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<PropsWithoutRef<P> & RefAttributes<T>>。这是一个组件,它采用与PropsWithoutRef<P>相交的类型的道具RefAttributes<T>。这两种类型也在同一个文件中定义。

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

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

鉴于这些信息,我真的不明白为什么我必须添加ref到我自己的Props. 它真的看起来好像forwardRef应该为我做 - 甚至实际上ref从我自己的身上移除Props......

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

标签: reactjstypescriptreact-hooks

解决方案


问题是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


推荐阅读