Property ‘current’ does not exist on type ‘((instance: HTMLDivElement | null) => void) | RefObject’

Usually when I use React + Typescript and I have to work with refs I usually use this check:

const ref = useRef<HTMLDivElement>(null)
...
if(ref && ref.current)

But recently, I’m receiving this error:
Property 'current' does not exist on type '((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement>'.
  Property 'current' does not exist on type '(instance: HTMLDivElement | null) => void'.ts(2339)

Any ideas on what this error means? To solve the issue, as a workaround, I’m adding a third check:
if(ref && "current" in ref && ref.current)

But this looks pretty bad, mainly when you have to work with multiple refs at once.

Thanks for your help.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

There are two kinds of refs in modern react: ref objects, and ref callbacks. Ref objects are what’s created by useRef (or in class components, createRef): it’s an object with a current property. In typescript, these have the type RefObject<T>, where T is whatever value(s) will be on current.

Ref callbacks are another option, which are needed for some advanced cases. You pass a function into the element, and the function will be called back when the instance is created or destroyed. These have the type (instance: T) => void.

A shorthand which combines both the ref object and the ref callback into a single type is Ref<T>, and it looks like that’s what your code expects. Since you haven’t shown that code, I’ll have to make some educated guesses about it looks like. Suppose you have a component which accepts a ref as a prop (perhaps so it can then hand it off to one of its internal components):

interface ExampleProps {
  buttonRef: Ref<HTMLButtonElement>
}

const Example: FC<ExampleProps> = ({ buttonRef }) => {
  return (
    <div>
      <button ref={buttonRef}>Hello</button>
    <div>
  )
}

Since I’ve defined the prop to be a Ref, it can be passed in either a ref object, or a ref callback. That’s fine in this case, since I’m not doing anything with it except passing it on to the button. But if I try to write some code to interact with it, I can’t assume it to be an object or a function.

If I need to do this, perhaps I could restrict the prop so it only takes ref objects, and then I can assume it will have .current

interface ExampleProps {
  buttonRef: RefObject<HTMLButtonElement>
}

const Example: FC<ExampleProps> = ({ buttonRef }) => {
  useEffect(() => {
    console.log(buttonRef.current);
  });
  return (
    <div>
      <button ref={buttonRef}>Hello</button>
    <div>
  )
}

But maybe I don’t want to restrict the way my component can be used, but I still need to be able to interact with the ref somehow. In that case, I’ll probably need to make a callback ref of my own, and then add logic to it to handle both my use of the ref, and the prop’s use of the ref:
interface ExampleProps {
  buttonRef: Ref<HTMLButtonElement>
}

const Example: FC<ExampleProps> = ({ buttonRef }) => {
  const myRef = useRef<HTMLButtonElement>(null);
  useEffect(() => {
    console.log(myRef.current);
  });
  return (
    <div>
      <button ref={(element) => {
        (myRef as MutableRefObject<HTMLButtonElement>).current = element;
        if (typeof buttonRef === 'function') {
          buttonRef(element);
        } else {
          buttonRef.current = element;
        }
      }}>Hello</button>
    <div>
  )
}

The type assertion of as MutableRefObject<HTMLButtonElement> is needed because myRef is marked as being immutable. This type reflects the fact that only react is supposed to modify the .current property. That’s good for normal use cases, but since we’re taking over that responsibility from react, it’s OK to change the value.

Method 2

Following Nicholas Tower’s answer, I did a reusable and typed util function to set both refs that might useful to others:

import { ForwardedRef, MutableRefObject } from 'react';

export const setComponentRefs =
 <T>(ref: MutableRefObject<T | null>, forwardedRef: ForwardedRef<T>) =>
 (el: T) => {
   ref.current = el;
   if (typeof forwardedRef === 'function') forwardedRef(el);
   else if (forwardedRef) forwardedRef.current = el;
};


///////// Example

<View ref={setComponentRefs(innerRef, ref)}>
 {...}
</View>


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x