Как использовать forwardRef с FunctionComponent в React Native с использованием TypeScript
Я просмотрел многие документы и примеры, но все еще не могу понять, как использовать 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>
)
}
Ответы
Пересылка 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}));
И теперь , наконец, это действительно работает !