From d3e837b4f6cc38a144de63323a095dbadbaf7ef7 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 13 Jan 2024 22:19:45 -0600 Subject: [PATCH] Refine MoreComponents data fetching --- src/app/page.tsx | 18 +++++--- src/components/MoreComponents.tsx | 68 +++++++++++++++++++++++++++---- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 42e8ce81..f51f46bc 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,4 +1,4 @@ -import { getPhotosCached } from '@/cache'; +import { getPhotosCached, getPhotosCountCached } from '@/cache'; import { generateOgImageMetaForPhotos } from '@/photo'; import PhotosEmptyState from '@/photo/PhotosEmptyState'; import { Metadata } from 'next'; @@ -14,20 +14,28 @@ export async function generateMetadata(): Promise { } export default async function HomePage() { - // Make homepage queries resilient to error on first time setup - const photos = await getPhotosCached({ limit: MAX_PHOTOS_TO_SHOW_OG }) - .catch(() => []); + const [ + photos, + count, + ] = await Promise.all([ + // Make homepage queries resilient to error on first time setup + getPhotosCached({ limit: MAX_PHOTOS_TO_SHOW_OG }).catch(() => []), + getPhotosCountCached().catch(() => 0), + ]); return ( photos.length > 0 ?
{ 'use server'; return ; }} /> diff --git a/src/components/MoreComponents.tsx b/src/components/MoreComponents.tsx index 534df222..f7cee02b 100644 --- a/src/components/MoreComponents.tsx +++ b/src/components/MoreComponents.tsx @@ -1,27 +1,81 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState, useTransition } from 'react'; +import Spinner from './Spinner'; export default function MoreComponents({ - initialOffset = 1, itemsPerRequest, + itemsTotalCount, componentLoader, + label = 'Load more', + triggerOnView = true, }: { - initialOffset?: number itemsPerRequest: number + itemsTotalCount: number componentLoader: (limit: number) => Promise + label?: string + triggerOnView?: boolean + prefetch?: boolean }) { - const [offset] = useState(initialOffset); + const [offset, setOffset] = useState(1); const [components, setComponents] = useState([]); + const [isPending, startTransition] = useTransition(); + + const buttonRef = useRef(null); + + const advance = useCallback(() => startTransition(() => { + setOffset(o => o + 1); + }), []); + useEffect(() => { - const getPhotosLargeComponentAsync = async () => { + const getMoreComponentsAsync = async () => { + console.log('getMoreComponentsAsync', itemsPerRequest * offset); return componentLoader(itemsPerRequest * offset); }; - getPhotosLargeComponentAsync().then((component) => { + getMoreComponentsAsync().then((component) => { setComponents([component]); }); }, [componentLoader, itemsPerRequest, offset]); - return components; + useEffect(() => { + // Only add observer if button is rendered + if (buttonRef.current) { + const observer = new IntersectionObserver(e => { + if ( + triggerOnView && + e[0].isIntersecting && + !isPending + ) { + advance(); + } + }, { + root: null, + threshold: 0, + }); + + observer.observe(buttonRef.current); + + return () => observer.disconnect(); + } + }, [triggerOnView, advance, isPending]); + + const showMoreButton = itemsTotalCount > itemsPerRequest * offset; + + return
+ {components} + {showMoreButton && + } +
; }