'use client'; import { useCallback, useEffect, useRef, useState } from 'react'; import Spinner from './Spinner'; import SiteGrid from './SiteGrid'; export default function MoreComponents({ initialOffset, itemsPerRequest, getNextComponent, label = 'Load more', triggerOnView = true, prefetch = true, }: { initialOffset: number itemsPerRequest: number getNextComponent: (offset: number, limit: number) => Promise<{ nextComponent: JSX.Element, isFinished: boolean, }> label?: string triggerOnView?: boolean prefetch?: boolean }) { const [indexToView, setIndexToView] = useState(0); const [indexLoaded, setIndexLoaded] = useState(0); const [isLoading, setIsLoading] = useState(false); const [lastIndexToLoad, setLastIndexToLoad] = useState(); const [components, setComponents] = useState([]); const indexToLoad = lastIndexToLoad ?? (prefetch ? indexToView + 1 : indexToView); useEffect(() => { if ( !isLoading && // (lastIndexToLoad === undefined || indexToLoad <= lastIndexToLoad) && // indexToLoad >= 1 && indexToLoad > indexToView && indexToLoad > indexLoaded ) { setIsLoading(true); getNextComponent( initialOffset + (indexToLoad - 1) * itemsPerRequest, itemsPerRequest, ) .then(({ nextComponent, isFinished }) => { setComponents(current => { const updatedComponents = [...current]; updatedComponents[indexToLoad] = nextComponent; return updatedComponents; }); setIndexLoaded(indexToLoad); if (isFinished) { setLastIndexToLoad(indexToLoad); } }) .finally(() => setIsLoading(false)); } }, [ isLoading, // lastIndexToLoad, indexToLoad, indexToView, indexLoaded, getNextComponent, initialOffset, itemsPerRequest, ]); const buttonRef = useRef(null); const advance = useCallback(() => { if (indexToView < indexLoaded) { setIndexToView(i => i + 1); } }, [indexToView, indexLoaded]); useEffect(() => { // Only add observer if button is rendered if (buttonRef.current) { const observer = new IntersectionObserver(e => { if (triggerOnView && e[0].isIntersecting) { advance(); } }, { root: null, threshold: 0, }); observer.observe(buttonRef.current); return () => observer.disconnect(); } }, [triggerOnView, advance]); // console.log({ // indexToLoad, // indexToView, // // indexLoaded, // lastIndexToLoad, // componentsLength: components.length, // componentsToView: components.slice(0, indexToView + 1).length, // }); return <> {components.slice(0, indexToView + 1)} {indexToView < indexLoaded && {isLoading ? : label} } />} ; }