Skip to content

Commit

Permalink
Merge pull request #27 from ethanniser/optimize
Browse files Browse the repository at this point in the history
Optimize image prefetching
  • Loading branch information
RhysSullivan authored Oct 21, 2024
2 parents 647552d + 1323762 commit 6b045f6
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 7 deletions.
8 changes: 5 additions & 3 deletions src/app/(category-sidebar)/[collection]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import { count } from "drizzle-orm";
import Image from "next/image";

export default async function Home(props: {
params: {
params: Promise<{
collection: string;
};
}>;
}) {
const collectionName = decodeURIComponent((await props.params).collection);

const collections = await db.query.collections.findMany({
with: {
categories: true,
},
where: (collections, { eq }) =>
eq(collections.name, decodeURIComponent(props.params.collection)),
eq(collections.name, decodeURIComponent(collectionName)),
orderBy: (collections, { asc }) => asc(collections.name),
});
let imageCount = 0;
Expand Down
43 changes: 39 additions & 4 deletions src/components/ui/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import NextLink from "next/link";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";

type PrefetchImage = {
srcset: string;
Expand Down Expand Up @@ -38,17 +38,52 @@ const seen = new Set<string>();

export const Link: typeof NextLink = (({ children, ...props }) => {
const [images, setImages] = useState<PrefetchImage[]>([]);
const linkRef = useRef<HTMLAnchorElement>(null);
const router = useRouter();
let prefetchTimeout: NodeJS.Timeout | null = null; // Track the timeout ID

useEffect(() => {
if (props.prefetch === false) {
return;
}
void prefetchImages(String(props.href)).then((images) => {
setImages(images);
}, console.error);

const linkElement = linkRef.current;
if (!linkElement) return;

const observer = new IntersectionObserver(
(entries) => {
const entry = entries[0];
if (entry.isIntersecting) {
// Set a timeout to trigger prefetch after 1 second
prefetchTimeout = setTimeout(() => {
void prefetchImages(String(props.href)).then((images) => {
setImages(images);
}, console.error);
// Stop observing once images are prefetched
observer.unobserve(entry.target);
}, 300); // 300ms delay
} else if (prefetchTimeout) {
// If the element leaves the viewport before 1 second, cancel the prefetch
clearTimeout(prefetchTimeout);
prefetchTimeout = null;
}
},
{ rootMargin: "0px", threshold: 0.1 }, // Trigger when at least 10% is visible
);

observer.observe(linkElement);

return () => {
observer.disconnect(); // Cleanup the observer when the component unmounts
if (prefetchTimeout) {
clearTimeout(prefetchTimeout); // Clear any pending timeouts when component unmounts
}
};
}, [props.href, props.prefetch]);

return (
<NextLink
ref={linkRef}
onMouseOver={() => {
for (const image of images) {
if (image.loading === "lazy" || seen.has(image.srcset)) {
Expand Down

0 comments on commit 6b045f6

Please sign in to comment.