Forwarding the Ref
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.
Using the Ref
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!