score:18

Accepted answer

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!


Related Query

More Query from same tag