11import React from 'react' ;
22
3- import { calculateElementOffsetTop , rafThrottle } from './utils' ;
3+ import { rafThrottle } from './utils' ;
44
55interface UseScrollBasedChunksProps {
66 scrollContainerRef : React . RefObject < HTMLElement > ;
77 tableRef : React . RefObject < HTMLElement > ;
88 totalItems : number ;
99 rowHeight : number ;
1010 chunkSize : number ;
11- overscanCount ?: number ;
11+ renderOverscan ?: number ;
12+ fetchOverscan ?: number ;
13+ tableOffset : number ;
1214}
1315
14- const DEFAULT_OVERSCAN_COUNT = 2 ;
16+ interface ChunkState {
17+ shouldRender : boolean ;
18+ shouldFetch : boolean ;
19+ }
20+
21+ const isSafari = / ^ ( (? ! c h r o m e | a n d r o i d ) .) * s a f a r i / i. test ( navigator . userAgent ) ;
22+
23+ // Bad performance in Safari - reduce overscan counts
24+ const DEFAULT_RENDER_OVERSCAN = isSafari ? 1 : 2 ;
25+ const DEFAULT_FETCH_OVERSCAN = isSafari ? 2 : 4 ;
1526
1627export const useScrollBasedChunks = ( {
1728 scrollContainerRef,
1829 tableRef,
1930 totalItems,
2031 rowHeight,
2132 chunkSize,
22- overscanCount = DEFAULT_OVERSCAN_COUNT ,
23- } : UseScrollBasedChunksProps ) : boolean [ ] => {
33+ tableOffset,
34+ renderOverscan = DEFAULT_RENDER_OVERSCAN ,
35+ fetchOverscan = DEFAULT_FETCH_OVERSCAN ,
36+ } : UseScrollBasedChunksProps ) : ChunkState [ ] => {
2437 const chunksCount = React . useMemo (
2538 ( ) => Math . ceil ( totalItems / chunkSize ) ,
2639 [ chunkSize , totalItems ] ,
2740 ) ;
2841
29- const [ startChunk , setStartChunk ] = React . useState ( 0 ) ;
30- const [ endChunk , setEndChunk ] = React . useState (
31- Math . min ( overscanCount , Math . max ( chunksCount - 1 , 0 ) ) ,
32- ) ;
42+ const [ visibleStartChunk , setVisibleStartChunk ] = React . useState ( 0 ) ;
43+ const [ visibleEndChunk , setVisibleEndChunk ] = React . useState ( 0 ) ;
3344
3445 const calculateVisibleRange = React . useCallback ( ( ) => {
3546 const container = scrollContainerRef ?. current ;
@@ -38,24 +49,23 @@ export const useScrollBasedChunks = ({
3849 return null ;
3950 }
4051
41- const tableOffset = calculateElementOffsetTop ( table , container ) ;
4252 const containerScroll = container . scrollTop ;
4353 const visibleStart = Math . max ( containerScroll - tableOffset , 0 ) ;
4454 const visibleEnd = visibleStart + container . clientHeight ;
4555
46- const start = Math . max ( Math . floor ( visibleStart / rowHeight / chunkSize ) - overscanCount , 0 ) ;
56+ const start = Math . max ( Math . floor ( visibleStart / rowHeight / chunkSize ) , 0 ) ;
4757 const end = Math . min (
48- Math . floor ( visibleEnd / rowHeight / chunkSize ) + overscanCount ,
58+ Math . floor ( visibleEnd / rowHeight / chunkSize ) ,
4959 Math . max ( chunksCount - 1 , 0 ) ,
5060 ) ;
5161 return { start, end} ;
52- } , [ scrollContainerRef , tableRef , rowHeight , chunkSize , overscanCount , chunksCount ] ) ;
62+ } , [ scrollContainerRef , tableRef , tableOffset , rowHeight , chunkSize , chunksCount ] ) ;
5363
5464 const updateVisibleChunks = React . useCallback ( ( ) => {
5565 const newRange = calculateVisibleRange ( ) ;
5666 if ( newRange ) {
57- setStartChunk ( newRange . start ) ;
58- setEndChunk ( newRange . end ) ;
67+ setVisibleStartChunk ( newRange . start ) ;
68+ setVisibleEndChunk ( newRange . end ) ;
5969 }
6070 } , [ calculateVisibleRange ] ) ;
6171
@@ -94,11 +104,28 @@ export const useScrollBasedChunks = ({
94104 } , [ handleScroll , scrollContainerRef ] ) ;
95105
96106 return React . useMemo ( ( ) => {
97- // boolean array that represents active chunks
98- const activeChunks = Array ( chunksCount ) . fill ( false ) ;
99- for ( let i = startChunk ; i <= endChunk ; i ++ ) {
100- activeChunks [ i ] = true ;
101- }
102- return activeChunks ;
103- } , [ chunksCount , startChunk , endChunk ] ) ;
107+ // Calculate render range (visible + render overscan)
108+ const renderStartChunk = Math . max ( visibleStartChunk - renderOverscan , 0 ) ;
109+ const renderEndChunk = Math . min (
110+ visibleEndChunk + renderOverscan ,
111+ Math . max ( chunksCount - 1 , 0 ) ,
112+ ) ;
113+
114+ // Calculate fetch range (visible + fetch overscan)
115+ const fetchStartChunk = Math . max ( visibleStartChunk - fetchOverscan , 0 ) ;
116+ const fetchEndChunk = Math . min (
117+ visibleEndChunk + fetchOverscan ,
118+ Math . max ( chunksCount - 1 , 0 ) ,
119+ ) ;
120+
121+ // Create chunk states array
122+ const chunkStates : ChunkState [ ] = Array ( chunksCount )
123+ . fill ( null )
124+ . map ( ( _ , index ) => ( {
125+ shouldRender : index >= renderStartChunk && index <= renderEndChunk ,
126+ shouldFetch : index >= fetchStartChunk && index <= fetchEndChunk ,
127+ } ) ) ;
128+
129+ return chunkStates ;
130+ } , [ chunksCount , visibleStartChunk , visibleEndChunk , renderOverscan , fetchOverscan ] ) ;
104131} ;
0 commit comments