1- import React , { useEffect , useRef , useCallback } from "react" ;
1+ "use client" ;
2+
3+ import React , { useEffect , useRef , useCallback , useState } from "react" ;
24import {
35 useInfiniteHits ,
46 useInstantSearch ,
@@ -34,29 +36,64 @@ const transformItems = (items: any[]) => {
3436} ;
3537
3638export function ApiGrid ( { gridColumns, pageSize } : ApiGridProps ) {
37- const { query } = useSearchBox ( ) ; // <-- Get current search term
39+ const { query } = useSearchBox ( ) ;
3840 const { status, error } = useInstantSearch ( { catchError : true } ) ;
39- const { hits, isLastPage, showMore } = useInfiniteHits ( {
40- transformItems,
41- showPrevious : false ,
42- } ) ;
41+ const {
42+ hits,
43+ isLastPage,
44+ showMore : originalShowMore ,
45+ } = useInfiniteHits (
46+ {
47+ transformItems,
48+ showPrevious : false ,
49+ } ,
50+ { skipSuspense : true }
51+ ) ;
52+
53+ const [ hasInitiallyLoaded , setHasInitiallyLoaded ] = useState ( false ) ;
54+ const [ isLoadingMore , setIsLoadingMore ] = useState ( false ) ;
55+ const observerRef = useRef < HTMLDivElement | null > ( null ) ;
56+ const prevQueryRef = useRef ( query ) ;
4357
4458 const loading = status === "loading" ;
4559 const stalled = status === "stalled" ;
4660 const hasError = status === "error" ;
47- const initialLoading = ( loading || stalled ) && hits . length === 0 ;
48- const loadingMore = ( loading || stalled ) && hits . length > 0 ;
61+ const isSearching = loading || stalled ;
4962 const hasMore = ! isLastPage && ! hasError ;
5063
51- const observerRef = useRef < HTMLDivElement | null > ( null ) ;
64+ const showMore = useCallback ( ( ) => {
65+ if ( hasMore && ! isSearching ) {
66+ setIsLoadingMore ( true ) ;
67+ originalShowMore ( ) ;
68+ }
69+ } , [ hasMore , isSearching , originalShowMore ] ) ;
70+
71+ useEffect ( ( ) => {
72+ if ( isLoadingMore && status === "idle" ) {
73+ setIsLoadingMore ( false ) ;
74+ }
75+ } , [ isLoadingMore , status ] ) ;
76+
77+ useEffect ( ( ) => {
78+ if ( query !== prevQueryRef . current ) {
79+ setIsLoadingMore ( false ) ;
80+ prevQueryRef . current = query ;
81+ }
82+ } , [ query ] ) ;
83+
84+ useEffect ( ( ) => {
85+ if ( ! isSearching && hits . length >= 0 ) {
86+ setHasInitiallyLoaded ( true ) ;
87+ }
88+ } , [ isSearching , hits . length ] ) ;
5289
5390 const handleIntersection = useCallback (
5491 ( entries : IntersectionObserverEntry [ ] ) => {
55- if ( entries [ 0 ] . isIntersecting && hasMore && ! loading && ! stalled ) {
92+ if ( entries [ 0 ] . isIntersecting && hasMore && ! isSearching ) {
5693 showMore ( ) ;
5794 }
5895 } ,
59- [ hasMore , loading , stalled , showMore ] ,
96+ [ hasMore , isSearching , showMore ]
6097 ) ;
6198
6299 useEffect ( ( ) => {
@@ -69,10 +106,7 @@ export function ApiGrid({ gridColumns, pageSize }: ApiGridProps) {
69106 } ) ;
70107
71108 intersectionObserver . observe ( observer ) ;
72-
73- return ( ) => {
74- intersectionObserver . disconnect ( ) ;
75- } ;
109+ return ( ) => intersectionObserver . disconnect ( ) ;
76110 } , [ handleIntersection ] ) ;
77111
78112 if ( hasError && error ) {
@@ -85,36 +119,45 @@ export function ApiGrid({ gridColumns, pageSize }: ApiGridProps) {
85119 ) ;
86120 }
87121
122+ const shouldShowInitialSkeleton =
123+ isSearching && ! hasInitiallyLoaded && hits . length === 0 ;
124+ const shouldShowNoResults =
125+ hits . length === 0 && hasInitiallyLoaded && query . length > 0 ;
126+
88127 return (
89128 < section id = "apis-list" className = "cards" >
90- { initialLoading ? (
91- < div className = "grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4 " >
129+ { stalled && (
130+ < div className = "fixed top-0 left-0 w-full h-1 bg-blue-500 animate-pulse z-50" />
131+ ) }
132+
133+ { shouldShowInitialSkeleton ? (
134+ < div className = "grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4" >
92135 { Array . from ( { length : Math . min ( pageSize , gridColumns * 2 ) } ) . map (
93136 ( _ , index ) => (
94137 < CardSkeleton key = { `skeleton-loading-${ index } ` } />
95- ) ,
138+ )
96139 ) }
97140 </ div >
98141 ) : (
99142 < >
100- < div className = "grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4 " >
143+ < div className = "grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4" >
101144 { hits . length > 0 ? (
102145 hits . map ( ( hit , index ) => (
103146 < Card key = { `${ hit . objectID } -${ index } ` } model = { hit } />
104147 ) )
105- ) : (
148+ ) : shouldShowNoResults ? (
106149 < div className = "col-span-full text-center py-6 bg-gray-50 rounded-lg border border-gray-100" >
107150 No APIs found matching "{ query } "
108151 </ div >
109- ) }
152+ ) : null }
110153 </ div >
111154
112- { loadingMore && (
113- < div className = "grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4 " >
155+ { isLoadingMore && (
156+ < div className = "grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4 mt-4 " >
114157 { Array . from ( { length : Math . min ( pageSize , gridColumns ) } ) . map (
115158 ( _ , index ) => (
116159 < CardSkeleton key = { `skeleton-more-${ index } ` } />
117- ) ,
160+ )
118161 ) }
119162 </ div >
120163 ) }
0 commit comments