В React, используя TypeScript, как передать ссылку на пользовательский компонент, используя RefForwardingComponent и forwardRef? - PullRequest
0 голосов
/ 04 февраля 2020

Я пытаюсь передать ссылку на пользовательский компонент (чтобы я мог установить фокус на компонент).

Но я продолжаю получать эту ошибку

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 как часть реквизита, так и в качестве второго аргумента обратного вызова функции, переданной forwardRef

Моя проблема с этим, вероятно, будет яснее, если я напишу это так this

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 */ }
    />
));

EDIT 2:

Вот связанный вопрос

Typescript 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 ...

Может кто-нибудь объяснить, что здесь происходит?

1 Ответ

0 голосов
/ 10 марта 2020

Проблема в неверном определении типа для 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...