Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

React ForwardRef: Property 'current' does not exist on type 'ForwardedRef<HTMLElement>'

I am trying to create a component that will track the vertical scroll. The catch is – the actual scroll container is not easily predictable (in this specific case it is neither window, document nor body – it is div#__next, due to CSS overflow rules).

I want to keep the component flexible and self-contained. So I’ve created a ref with DOM selector as an argument. I know it is far from idiomatic (to say the least), but it suprisingly seems to be working:

// Parent component
import { useRef } from "react"

const Article = (props) => {
  const scrollContainerRef = useRef<HTMLElement | null>(
    document.querySelector("#__next") // <-- the scroll container reference
  )

  return (
    <SomeContent>
      <ScrollToTop treshold={640} ref={scrollContainerRef} />
    </SomeContent>
)
// ScrollToTop
const ScrollToTop = forwardRef(
  ({ treshold }, ref) => {
    const [visible, setVisible] = useState(false)

    useEffect(() => {

      if (ref?.current) {
        ref.current.addEventListener("scroll", throttle(toggleVisible, 300))
        return () => {
          ref.current.removeEventListener("scroll", throttle(toggleVisible, 300))
        }
      }
    }, [])
// …

So what’s the problem? the current one is Typescript. I’ve spent hours trying to get the types right, but to no avail. The parent component is red squigly lines free (unless I pass globalThis, which seems to work at least in CodeSandbox), but the ScrollToTop is compaining whenever I am accessing current property:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Property 'current' does not exist on type 'ForwardedRef<HTMLElement>'.

I’ve tried to use React.MutableRefObject<HTMLElement | null /* or other T's */>, both in parent and in child, but it didn’t help.

Any ideas how to get the types to match? Or is this a silly idea from the beginning?

CodeSandbox demo

>Solution :

Refs might be objects with a .current property, but they might also be functions. So you can’t assume that a forwarded ref has a .current property.

I think it’s a mistake to use forwardRef at all here. The purpose of forwardRef is to allow a parent component to get access to an element in a child component. But instead, the parent is the one finding the element, and then you’re passing it to the child for it to use. I would use a regular state and prop for that:

const Article = (props) => {
  const [scrollContainer, setScrollContainer] = useState<HTMLElement | null>(() => {
    return document.querySelector("#__next");
  });

  return (
    <SomeContent>
      <ScrollToTop treshold={640} scrollContainer={scrollContainer} />
    </SomeContent>
)

interface ScrollToTopProps {
  treshold: number;
  scrollContainer: HTMLElement | null;
}

const ScrollToTop = ({ treshold, scrollContainer }: ScrollToTopProps) => {
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    if (scrollContainer) {
      const toggle = throttle(toggleVisible, 300);
      scrollContainer.addEventListener("scroll", toggle);
      return () => {
        scrollContainer.removeEventListener("scroll", toggle);
      }
    }
  }, [scrollContainer]);
  // ...
}

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading