1
- import type { ForwardedRef , MutableRefObject } from 'react' ;
2
- import React , { forwardRef , useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
1
+ import type { ForwardedRef } from 'react' ;
2
+ import React , { forwardRef , useCallback , useEffect , useMemo , useState } from 'react' ;
3
3
import type { FlatListProps , ListRenderItem , ListRenderItemInfo , FlatList as RNFlatList , ScrollViewProps } from 'react-native' ;
4
4
import FlatList from '@components/FlatList' ;
5
5
import usePrevious from '@hooks/usePrevious' ;
6
6
import getInitialPaginationSize from './getInitialPaginationSize' ;
7
+ import RenderTaskQueue from './RenderTaskQueue' ;
7
8
8
9
// Adapted from https://github.com/facebook/react-native/blob/29a0d7c3b201318a873db0d1b62923f4ce720049/packages/virtualized-lists/Lists/VirtualizeUtils.js#L237
9
10
function defaultKeyExtractor < T > ( item : T | { key : string } | { id : string } , index : number ) : string {
@@ -26,7 +27,6 @@ type BaseInvertedFlatListProps<T> = Omit<FlatListProps<T>, 'data' | 'renderItem'
26
27
} ;
27
28
28
29
const AUTOSCROLL_TO_TOP_THRESHOLD = 250 ;
29
- const RENDER_DELAY = 500 ;
30
30
31
31
function BaseInvertedFlatList < T > ( props : BaseInvertedFlatListProps < T > , ref : ForwardedRef < RNFlatList > ) {
32
32
const { shouldEnableAutoScrollToTopThreshold, initialScrollKey, data, onStartReached, renderItem, keyExtractor = defaultKeyExtractor , ...rest } = props ;
@@ -55,45 +55,28 @@ function BaseInvertedFlatList<T>(props: BaseInvertedFlatListProps<T>, ref: Forwa
55
55
const dataIndexDifference = data . length - displayedData . length ;
56
56
57
57
// Queue up updates to the displayed data to avoid adding too many at once and cause jumps in the list.
58
- const queuedRenders = useRef < Array < { distanceFromStart : number } > > ( [ ] ) ;
59
- const isRendering = useRef ( false ) ;
60
-
61
- const renderTimeout = useRef < NodeJS . Timeout > ( ) ;
58
+ const renderQueue = useMemo ( ( ) => new RenderTaskQueue ( ) , [ ] ) ;
62
59
useEffect ( ( ) => {
63
60
return ( ) => {
64
- clearTimeout ( renderTimeout . current ) ;
61
+ renderQueue . cancel ( ) ;
65
62
} ;
66
- } , [ ] ) ;
67
-
68
- // Use a ref here to make sure we always operate on the latest state.
69
- const updateDisplayedDataRef = useRef ( ) as MutableRefObject < ( ) => void > ;
70
- // eslint-disable-next-line react-compiler/react-compiler
71
- updateDisplayedDataRef . current = ( ) => {
72
- const info = queuedRenders . current . shift ( ) ;
73
- if ( ! info ) {
74
- isRendering . current = false ;
75
- return ;
76
- }
77
- isRendering . current = true ;
63
+ } , [ renderQueue ] ) ;
78
64
65
+ renderQueue . setHandler ( ( info ) => {
79
66
if ( ! isLoadingData ) {
80
67
onStartReached ?.( info ) ;
81
68
}
82
69
setIsInitialData ( false ) ;
83
70
const firstDisplayedItem = displayedData . at ( 0 ) ;
84
71
setCurrentDataId ( firstDisplayedItem ? keyExtractor ( firstDisplayedItem , currentDataIndex ) : '' ) ;
72
+ } ) ;
85
73
86
- renderTimeout . current = setTimeout ( ( ) => {
87
- updateDisplayedDataRef . current ( ) ;
88
- } , RENDER_DELAY ) ;
89
- } ;
90
-
91
- const handleStartReached = useCallback ( ( info : { distanceFromStart : number } ) => {
92
- queuedRenders . current . push ( info ) ;
93
- if ( ! isRendering . current ) {
94
- updateDisplayedDataRef . current ( ) ;
95
- }
96
- } , [ ] ) ;
74
+ const handleStartReached = useCallback (
75
+ ( info : { distanceFromStart : number } ) => {
76
+ renderQueue . add ( info ) ;
77
+ } ,
78
+ [ renderQueue ] ,
79
+ ) ;
97
80
98
81
const handleRenderItem = useCallback (
99
82
( { item, index, separators} : ListRenderItemInfo < T > ) => {
0 commit comments