Как использовать forwardRef с FunctionComponent в React Native с использованием TypeScript

Oct 22 2020

Я просмотрел многие документы и примеры, но все еще не могу понять, как использовать forwardRefфункциональный компонент с TypeScript в React Native. Ниже приведен пример, в котором я создаю MyCustomComponentпользовательскую функцию, которую пытаюсь вызвать из родительского объекта, создав ссылку. Однако, поскольку ссылка определена неправильно, и nullя, очевидно, получаю сообщение об ошибке, говорящее мне, что функция не существует. Пожалуйста, помогите мне понять, как правильно использовать forwardRefReact Native. Заранее спасибо!

interface MyCustomComponentProps {
    title: string
}

const MyCustomComponent: React.FunctionComponent<MyCustomComponentProps> = React.forwardRef((props, ref) => {
    const coolAlert = () => {
        Alert.alert('Hey!', 'This was called from MyCustomComponent')
    }
    return (
        <View>
            <Text>{props.title}</Text>
        </View>
    )
})

export default function App () {
    const MyCustomComponentRef = useRef()
    return (
        <SafeAreaView>
            <MyCustomComponent ref={MyCustomComponentRef} title='Hello World' />
            <TouchableOpacity
                onPress={() => {
                    MyCustomComponentRef.coolAlert()
                }}>
                <Text>Click Me</Text>
            </TouchableOpacity>
        </SafeAreaView>
    )
}

Ответы

3 LindaPaiste Oct 22 2020 at 23:27

Пересылка Ref

Ссылки могут сбивать с толку, потому что существует несколько способов их обработки и потому, что люди не знают разницы между объектом ref ( React.MutableRefObjectили React.RefObject) и значением ref, которое хранится в .currentсвойстве объекта ref. Вы допустили здесь эту ошибку вместе с некоторыми недостающими или неправильными типами машинописного текста.

useRef<T>- это общая ловушка, в которой значение Tсообщает, какой тип значения будет сохранен. Нам нужно сказать, Appчто мы намерены сохранить что-то с помощью coolAlertметода. На самом деле позже мы увидим, что нам нужно, чтобы наша ссылка была неизменной, поэтому мы будем использовать createRef<T>вместо нее .

interface MyRef {
  coolAlert(): void;
}

const MyCustomComponentRef = createRef<MyRef>();

Когда мы вызываем onPress, нам нужно получить доступ к текущему значению объекта ref. При добавлении универсального к createRef, машинописный текст уже знает, что это значение либо MyRefили undefined. Мы можем вызвать coolAlertс помощью необязательного ?.оператора цепочки .

onPress={() => MyCustomComponentRef.current?.coolAlert()}

Теперь нам нужно немного поработать MyCustomComponent. Вы ошиблись, присвоив ему тип, React.FunctionComponent<MyCustomComponentProps>потому что функциональный компонент не имеет необходимых нам знаний о пересылке ссылок.

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

Тип для MyCustomComponentдолжен быть сложным возвращаемым типом из forwardRef. Но мы не должны назначать себе тот тип, нам просто нужно пройти дженерики Tи Pк forwardRefвызову функции. T- это тип ссылки и Pтип реквизита.

const MyCustomComponent = React.forwardRef<MyRef, MyCustomComponentProps>(...

Итак, мы избавились от всех ошибок машинописного текста ! Ура! Кроме ... подожди. На самом деле он ничего не делает . Все это и все равно не работает. Ненавижу рефери. Рефлы плохие.

Использование Ref

Мы перенаправили ссылку тому MyCustomComponent, кто теперь имеет доступ к перенаправленной ссылке и может прикрепить ее к компоненту DOM. Но мы не хотим, чтобы он был прикреплен к элементу DOM, мы хотим, чтобы он был прикреплен MyCustomComponent. Но мы не можем этого сделать.

По умолчанию вы не можете использовать атрибут ref в функциональных компонентах, потому что у них нет экземпляров [docs]

Мы должны использовать ловушку, useImperativeHandleкоторая выглядит как решение для взлома, и даже в документации говорится: «Не делайте этого». Ага, ненавижу рефери.

useImperativeHandle настраивает значение экземпляра, которое предоставляется родительским компонентам при использовании ref. Как всегда, в большинстве случаев следует избегать императивного кода, использующего ссылки. useImperativeHandle следует использовать с forwardRef. [документы]

Мы должны раскрыть наш coolAlertметод useImperativeHandle.

useImperativeHandle(ref , () => ({coolAlert}));

И теперь , наконец, это действительно работает !