@@ -11,6 +11,7 @@ import {
11
11
Platform ,
12
12
FlatList ,
13
13
ListRenderItemInfo ,
14
+ ViewToken ,
14
15
} from 'react-native' ;
15
16
import TabBarItem , { Props as TabBarItemProps } from './TabBarItem' ;
16
17
import TabBarIndicator , { Props as IndicatorProps } from './TabBarIndicator' ;
@@ -247,6 +248,10 @@ const renderIndicatorDefault = (props: IndicatorProps<Route>) => (
247
248
248
249
const getTestIdDefault = ( { route } : Scene < Route > ) => route . testID ;
249
250
251
+ // How many items measurements should we update per batch.
252
+ // Defaults to 10, since that's whats FlatList is using in initialNumToRender.
253
+ const MEASURE_PER_BATCH = 10 ;
254
+
250
255
export default function TabBar < T extends Route > ( {
251
256
getLabelText = getLabelTextDefault ,
252
257
getAccessible = getAccessibleDefault ,
@@ -279,8 +284,9 @@ export default function TabBar<T extends Route>({
279
284
} : Props < T > ) {
280
285
const [ layout , setLayout ] = React . useState < Layout > ( { width : 0 , height : 0 } ) ;
281
286
const [ tabWidths , setTabWidths ] = React . useState < Record < string , number > > ( { } ) ;
282
- const flatListRef = React . useRef < FlatList > ( null ) ;
287
+ const flatListRef = React . useRef < FlatList | null > ( null ) ;
283
288
const isFirst = React . useRef ( true ) ;
289
+ const howManyMeasured = React . useRef ( 0 ) ;
284
290
const scrollAmount = useAnimatedValue ( 0 ) ;
285
291
const measuredTabWidths = React . useRef < Record < string , number > > ( { } ) ;
286
292
@@ -298,7 +304,14 @@ export default function TabBar<T extends Route>({
298
304
299
305
const hasMeasuredTabWidths =
300
306
Boolean ( layout . width ) &&
301
- routes . every ( ( r ) => typeof tabWidths [ r . key ] === 'number' ) ;
307
+ routes
308
+ . slice (
309
+ 0 ,
310
+ routes . length > MEASURE_PER_BATCH
311
+ ? howManyMeasured . current
312
+ : routes . length
313
+ )
314
+ . every ( ( r ) => typeof tabWidths [ r . key ] === 'number' ) ;
302
315
303
316
React . useEffect ( ( ) => {
304
317
if ( isFirst . current ) {
@@ -373,13 +386,25 @@ export default function TabBar<T extends Route>({
373
386
? ( e : LayoutChangeEvent ) => {
374
387
measuredTabWidths . current [ route . key ] = e . nativeEvent . layout . width ;
375
388
376
- // When we have measured widths for all of the tabs, we should updates the state
377
- // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
389
+ // If we have more than 10 routes divide updating tabWidths into multiple batches. Here we update only first batch of 10 items.
378
390
if (
391
+ routes . length > MEASURE_PER_BATCH &&
392
+ index === MEASURE_PER_BATCH &&
393
+ routes
394
+ . slice ( 0 , MEASURE_PER_BATCH )
395
+ . every (
396
+ ( r ) => typeof measuredTabWidths . current [ r . key ] === 'number'
397
+ )
398
+ ) {
399
+ setTabWidths ( { ...measuredTabWidths . current } ) ;
400
+ howManyMeasured . current = MEASURE_PER_BATCH ;
401
+ } else if (
379
402
routes . every (
380
403
( r ) => typeof measuredTabWidths . current [ r . key ] === 'number'
381
404
)
382
405
) {
406
+ // When we have measured widths for all of the tabs, we should updates the state
407
+ // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
383
408
setTabWidths ( { ...measuredTabWidths . current } ) ;
384
409
}
385
410
}
@@ -494,6 +519,22 @@ export default function TabBar<T extends Route>({
494
519
[ scrollAmount ]
495
520
) ;
496
521
522
+ const handleViewableItemsChanged = React . useCallback (
523
+ ( { changed } : { changed : ViewToken [ ] } ) => {
524
+ if ( routes . length <= MEASURE_PER_BATCH ) {
525
+ return ;
526
+ }
527
+ // Get next vievable item
528
+ const [ item ] = changed ;
529
+ const index = item . index || 0 ;
530
+ if ( item . isViewable && index >= howManyMeasured . current ) {
531
+ setTabWidths ( { ...measuredTabWidths . current } ) ;
532
+ howManyMeasured . current += MEASURE_PER_BATCH ;
533
+ }
534
+ } ,
535
+ [ routes . length ]
536
+ ) ;
537
+
497
538
return (
498
539
< Animated . View onLayout = { handleLayout } style = { [ styles . tabBar , style ] } >
499
540
< Animated . View
@@ -513,6 +554,7 @@ export default function TabBar<T extends Route>({
513
554
position,
514
555
layout,
515
556
navigationState,
557
+ visible : hasMeasuredTabWidths ,
516
558
jumpTo,
517
559
width : isWidthDynamic
518
560
? 'auto'
@@ -535,6 +577,7 @@ export default function TabBar<T extends Route>({
535
577
data = { routes as Animated . WithAnimatedValue < T > [ ] }
536
578
keyExtractor = { keyExtractor }
537
579
horizontal
580
+ initialNumToRender = { MEASURE_PER_BATCH }
538
581
accessibilityRole = "tablist"
539
582
keyboardShouldPersistTaps = "handled"
540
583
scrollEnabled = { scrollEnabled }
@@ -549,6 +592,7 @@ export default function TabBar<T extends Route>({
549
592
scrollEventThrottle = { 16 }
550
593
renderItem = { renderItem }
551
594
onScroll = { handleScroll }
595
+ onViewableItemsChanged = { handleViewableItemsChanged }
552
596
ref = { flatListRef }
553
597
testID = { testID }
554
598
/>
0 commit comments