I've looked at many of the docs and examples but I still can't seem to quite understand how to use forwardRef
with a functional component with TypeScript in React Native. Below is an example where I create a MyCustomComponent
with a custom function that I try to call from the parent by creating a ref. However, since the ref is incorrectly defined and null
, I obviously get an error message telling me that the function doesn't exist. Please help me understand how to properly use forwardRef
in React Native. Thanks in advance!
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>
)
}
Here's the code for doing so: import React, { useState, useRef, forwardRef } from 'React'; const Input = forwardRef((props, ref) => { return <input ref={ref} {... props} />; }); const App = () => { const inputRef = useRef(null); const [value, setValue] = useState(''); const onInputChange = (e) => { e.
On this case, we need to change the Input Component to use the forwardRef. import React, { forwardRef } from "react"; const Input = (props, ref) => <input ref={ref} type="text" style={style} />; export default forwardRef(Input);
The forwardRef method in React allows parent components to move down (or “forward”) refs to their children. ForwardRef gives a child component a reference to a DOM entity created by its parent component in React. This helps the child to read and modify the element from any location where it is used.
Refs can be really confusing because there are multiple ways to handle them and because people aren't aware of the difference between the ref object (React.MutableRefObject
or React.RefObject
) and the ref value, which is stored on the .current
property of the ref object. You've made that mistake here, along with some missing or incorrect typescript types.
useRef<T>
is a generic hook where the value T
tells up what type of value will be stored. We need to tell App
that we intend to store something with a coolAlert
method. Actually we'll see later on that we need our ref to be immutable so we we'll use createRef<T>
instead.
interface MyRef {
coolAlert(): void;
}
const MyCustomComponentRef = createRef<MyRef>();
When we call onPress
, we need to access the current value of the ref object. By adding the generic to createRef
, typescript already knows that this value is either MyRef
or undefined
. We can call coolAlert
with the optional chaining ?.
operator.
onPress={() => MyCustomComponentRef.current?.coolAlert()}
Now we need to do some work on MyCustomComponent
. You've erred by assigning it the type React.FunctionComponent<MyCustomComponentProps>
because a function component doesn't have the knowledge about ref forwarding that we need.
function forwardRef<T, P = {}>(Component: RefForwardingComponent<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;
The type for MyCustomComponent
should be that complicated return type from forwardRef
. But we don't need to assign that type ourselves, we just need to pass the generics T
and P
to the forwardRef
function call. T
is the type of the ref and P
is the type of the props.
const MyCustomComponent = React.forwardRef<MyRef, MyCustomComponentProps>(...
Ok so we got rid of all the typescript errors! Yay! Except...hold up. It doesn't actually do anything. All of that and it still doesn't work. I hate refs. Refs are bad.
We forwarded the ref to MyCustomComponent
, who now has access to the forwarded ref and can attach it to a DOM component. But we don't want it attached to the DOM element, we want it attached to MyCustomComponent
. But we can't really do that.
By default, you may not use the ref attribute on function components because they don’t have instances [docs]
We have to make use of a hook called useImperativeHandle
which feels like a hack solution and even the docs say "don't do this". Yup, I hate refs.
useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with forwardRef. [docs]
We have to expose our coolAlert
method through useImperativeHandle
.
useImperativeHandle(ref , () => ({coolAlert}));
And now it actually works, finally!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With