Cómo usar forwardRef con FunctionComponent en React Native usando TypeScript
He visto muchos de los documentos y ejemplos, pero todavía parece que no puedo entender muy bien cómo usarlo forwardRefcon un componente funcional con TypeScript en React Native. A continuación se muestra un ejemplo en el que creo un MyCustomComponentcon una función personalizada que trato de llamar desde el padre creando una referencia. Sin embargo, dado que la referencia está definida incorrectamente y null, obviamente, aparece un mensaje de error que me dice que la función no existe. Ayúdame a entender cómo usar correctamente forwardRefReact Native. ¡Gracias por adelantado!
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>
)
}
Respuestas
Reenvío de la referencia
Las referencias pueden ser realmente confusas porque hay varias formas de manejarlas y porque las personas no son conscientes de la diferencia entre el objeto ref ( React.MutableRefObjecto React.RefObject) y el valor ref, que se almacena en la .currentpropiedad del objeto ref. Ha cometido ese error aquí, junto con algunos tipos de texto mecanografiados incorrectos o faltantes.
useRef<T>es un gancho genérico donde el valor Tindica qué tipo de valor se almacenará. Necesitamos decir Appque tenemos la intención de almacenar algo con un coolAlertmétodo. En realidad, veremos más adelante que necesitamos que nuestra referencia sea inmutable, por lo que usaremos createRef<T>en su lugar.
interface MyRef {
coolAlert(): void;
}
const MyCustomComponentRef = createRef<MyRef>();
Cuando llamamos onPress, necesitamos acceder al valor actual del objeto ref. Al agregar el genérico a createRef, mecanografiado ya sabe que este valor es MyRefo undefined. Podemos llamar coolAlertcon el ?.operador de encadenamiento opcional .
onPress={() => MyCustomComponentRef.current?.coolAlert()}
Ahora tenemos que trabajar un poco MyCustomComponent. Se ha equivocado al asignarle el tipo React.FunctionComponent<MyCustomComponentProps>porque un componente de función no tiene el conocimiento sobre el reenvío de referencias que necesitamos.
function forwardRef<T, P = {}>(Component: RefForwardingComponent<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;
El tipo de MyCustomComponentdebe ser ese tipo de retorno complicado de forwardRef. Pero no necesitamos asignar ese tipo nosotros mismos, solo necesitamos pasar los genéricos Ty Pla forwardRefllamada a la función. Tes el tipo de ref y Pes el tipo de utilería.
const MyCustomComponent = React.forwardRef<MyRef, MyCustomComponentProps>(...
Ok, ¡nos deshicimos de todos los errores de escritura ! ¡Hurra! Excepto ... espera. En realidad, no hace nada . Todo eso y todavía no funciona. Odio las referencias. Las referencias son malas.
Usando la Ref
Enviamos la referencia a MyCustomComponentquien ahora tiene acceso a la referencia reenviada y puede adjuntarla a un componente DOM. Pero no queremos que esté adjunto al elemento DOM, queremos que esté adjunto MyCustomComponent. Pero realmente no podemos hacer eso.
De forma predeterminada, no puede usar el atributo ref en los componentes de la función porque no tienen instancias [docs]
Tenemos que hacer uso de un gancho llamado useImperativeHandleque se siente como una solución de pirateo e incluso los documentos dicen "no hagas esto". Sí, odio a los árbitros.
useImperativeHandle personaliza el valor de instancia que se expone a los componentes principales cuando se usa ref. Como siempre, en la mayoría de los casos se debe evitar el uso de códigos imperativos con referencias. useImperativeHandle debe usarse con forwardRef. [docs]
Tenemos que exponer nuestro coolAlertmétodo useImperativeHandle.
useImperativeHandle(ref , () => ({coolAlert}));
¡Y ahora realmente funciona , finalmente!