diff --git a/packages/remix-react/components.tsx b/packages/remix-react/components.tsx index d2c7b94199f..1d271439a21 100644 --- a/packages/remix-react/components.tsx +++ b/packages/remix-react/components.tsx @@ -229,19 +229,37 @@ interface PrefetchHandlers { onTouchStart?: TouchEventHandler; } -function usePrefetchBehavior( +function usePrefetchBehavior( prefetch: PrefetchBehavior, theirElementProps: PrefetchHandlers -): [boolean, Required] { +): [boolean, React.RefObject, Required] { let [maybePrefetch, setMaybePrefetch] = React.useState(false); let [shouldPrefetch, setShouldPrefetch] = React.useState(false); let { onFocus, onBlur, onMouseEnter, onMouseLeave, onTouchStart } = theirElementProps; + let ref = React.useRef(null); + React.useEffect(() => { if (prefetch === "render") { setShouldPrefetch(true); } + + if (prefetch === "viewport") { + let callback: IntersectionObserverCallback = (entries) => { + console.log("entries", entries); + entries.forEach((entry) => { + if (entry.isIntersecting) setShouldPrefetch(true); + }); + }; + let observer = new IntersectionObserver(callback, { threshold: 0.5 }); + if (ref.current) observer.observe(ref.current); + else console.warn("No element to observe"); + + return () => { + observer.disconnect(); + }; + } }, [prefetch]); let setIntent = () => { @@ -270,6 +288,7 @@ function usePrefetchBehavior( return [ shouldPrefetch, + ref, { onFocus: composeEventHandlers(onFocus, setIntent), onBlur: composeEventHandlers(onBlur, cancelIntent), @@ -292,17 +311,18 @@ let NavLink = React.forwardRef( let isAbsolute = typeof to === "string" && ABSOLUTE_URL_REGEX.test(to); let href = useHref(to); - let [shouldPrefetch, prefetchHandlers] = usePrefetchBehavior( + let [shouldPrefetch, ref, prefetchHandlers] = usePrefetchBehavior( prefetch, props ); + return ( <> {shouldPrefetch && !isAbsolute ? ( @@ -324,48 +344,23 @@ let Link = React.forwardRef( ({ to, prefetch = "none", ...props }, forwardedRef) => { let isAbsolute = typeof to === "string" && ABSOLUTE_URL_REGEX.test(to); - let fallbackRef = React.useRef(null); - let href = useHref(to); - let [shouldPrefetch, prefetchHandlers] = usePrefetchBehavior( + let [shouldPrefetch, ref, prefetchHandlers] = usePrefetchBehavior( prefetch, props ); - let [shouldActuallyPrefetch, setShouldActuallyPrefetch] = - React.useState(shouldPrefetch); - - React.useEffect(() => { - if (prefetch === "viewport") { - let callback: IntersectionObserverCallback = (entries, observer) => { - console.log("entries", entries); - entries.forEach((entry) => { - if (entry.isIntersecting) { - setShouldActuallyPrefetch(true); - } - }); - }; - let observer = new IntersectionObserver(callback, { threshold: 0.5 }); - observer.observe(fallbackRef.current!); - - return () => { - observer.disconnect(); - }; - } - }, [forwardedRef, prefetch]); + console.log({ ref: ref.current, forwardedRef }); return ( <> { - forwardedRef = el; - fallbackRef.current = el; - }} - to={to} {...props} {...prefetchHandlers} + ref={mergeRefs(forwardedRef, ref)} + to={to} /> - {shouldActuallyPrefetch && !isAbsolute ? ( + {shouldPrefetch && !isAbsolute ? ( ) : null} @@ -1847,3 +1842,17 @@ export const LiveReload = /> ); }; + +function mergeRefs( + ...refs: Array | React.LegacyRef> +): React.RefCallback { + return (value) => { + refs.forEach((ref) => { + if (typeof ref === "function") { + ref(value); + } else if (ref != null) { + (ref as React.MutableRefObject).current = value; + } + }); + }; +}