From c695b26fdbe0b7dcfa76b82bab0a8ad6e6892033 Mon Sep 17 00:00:00 2001 From: Logan McAnsh Date: Fri, 19 May 2023 15:00:09 -0400 Subject: [PATCH] feat: initial viewport prefetching Signed-off-by: Logan McAnsh --- packages/remix-react/components.tsx | 33 ++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/remix-react/components.tsx b/packages/remix-react/components.tsx index 89fa1b7e9ab..d2c7b94199f 100644 --- a/packages/remix-react/components.tsx +++ b/packages/remix-react/components.tsx @@ -211,7 +211,7 @@ export function RemixRouteError({ id }: { id: string }) { * - "render": Fetched when the link is rendered * - "none": Never fetched */ -type PrefetchBehavior = "intent" | "render" | "none"; +type PrefetchBehavior = "intent" | "render" | "none" | "viewport"; export interface RemixLinkProps extends LinkProps { prefetch?: PrefetchBehavior; @@ -324,21 +324,48 @@ 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( 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]); + return ( <> { + forwardedRef = el; + fallbackRef.current = el; + }} to={to} {...props} {...prefetchHandlers} /> - {shouldPrefetch && !isAbsolute ? ( + {shouldActuallyPrefetch && !isAbsolute ? ( ) : null}