1
1
import type { PropsWithChildren } from "react"
2
2
import { IconLoader2 } from "@tabler/icons-react"
3
3
import type { UseInfiniteQueryResult } from "@tanstack/react-query"
4
+ import { useEffect , useRef } from "react"
4
5
5
6
import { ScrollArea } from "@/components/ui/scroll-area"
6
7
@@ -10,6 +11,10 @@ export const InfiniteScroll = ({
10
11
} : PropsWithChildren < {
11
12
query : UseInfiniteQueryResult
12
13
} > ) => {
14
+ const scrollRef = useRef < HTMLDivElement > ( null )
15
+ const contentRef = useRef < HTMLDivElement > ( null )
16
+
17
+ // Fetch more on scroll
13
18
const handleScroll = ( e : React . UIEvent < HTMLDivElement > ) => {
14
19
const { scrollTop, clientHeight, scrollHeight } = e . currentTarget
15
20
if ( scrollTop + clientHeight > scrollHeight - 100 ) {
@@ -20,17 +25,40 @@ export const InfiniteScroll = ({
20
25
}
21
26
}
22
27
28
+ // Check if viewport is filled and fetch more if needed
29
+ const checkAndFetchMore = ( ) => {
30
+ if ( ! scrollRef . current || ! contentRef . current ) return
31
+
32
+ const viewportHeight = scrollRef . current . clientHeight
33
+ const contentHeight = contentRef . current . clientHeight
34
+
35
+ // Fetch until it overflows a bit
36
+ const overflowThreshold = viewportHeight + 100
37
+
38
+ if ( contentHeight < overflowThreshold && query . hasNextPage && ! query . isFetching ) {
39
+ query . fetchNextPage ( )
40
+ }
41
+ }
42
+
43
+ useEffect ( ( ) => {
44
+ // Timeout for dom update
45
+ const timer = setTimeout ( checkAndFetchMore , 100 )
46
+ return ( ) => clearTimeout ( timer )
47
+ } , [ query . data ] )
48
+
23
49
return (
24
50
< ScrollArea
25
51
type = "always"
26
52
className = "block h-full w-full transition-all"
27
53
onScroll = { handleScroll }
54
+ ref = { scrollRef }
28
55
>
29
- { children }
56
+ < div ref = { contentRef } >
57
+ { children }
30
58
31
- { /* scroll trigger */ }
32
- < div className = "flex h-[100px] justify-center py-2 text-zinc-300" >
33
- { query . isFetching && < IconLoader2 className = "animate-spin" size = { 16 } /> }
59
+ < div className = "flex h-[100px] justify-center py-2 text-zinc-300" >
60
+ { query . isFetching && < IconLoader2 className = "animate-spin" size = { 16 } /> }
61
+ </ div >
34
62
</ div >
35
63
</ ScrollArea >
36
64
)
0 commit comments