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:
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
When using forwardRef the type of the ref argument is:
type ForwardedRef<T> = ((instance: T | null) => void) | MutableRefObject<T | null> | null;
Meaning the ref can be (1) a function, (2) a mutable object with current property or (3) null.
If you are sure your instance is MutableRefObject just use casting.
const MyInput = forwardRef<HTMLInputElement, MyProps>((props, ref) => {
...
const inputRef = ref as MutableRefObject<HTMLInputElement>;
const inputText = ref.current.value;
...
return <Input ref={ref} ...>;
});
P.S Please note that using as isn't type safe, so if you aren't sure what is the type of ref, some type checking is required.
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