Skip to content

Commit ad93104

Browse files
committed
Fix infinite loop for iOS
Reinstate rendering hack. `dataLocal` as a dependency was causing an infinite loop for iOS repeatedly clearing the data every 500ms. The rendering hacks need only trigger once on mount. Delay also reduced to be less perceptible.
1 parent 992e9de commit ad93104

File tree

2 files changed

+33
-3
lines changed

2 files changed

+33
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- added: Moonpay Sell support for ACH.
88
- added: Maestro `testID` support in `NotificationCard`
99
- changed: Scanning private key button copy updated from "Import" -> "Confirm"
10+
- fixed: iOS not immediately showing `EdgeCarousel` cards if only one card is present
1011
- fixed: `SceneWrapper` bottom inset calculations for scenes that do not `avoidKeyboard`
1112

1213
## 4.26.0 (2025-04-14)

src/components/common/EdgeCarousel.tsx

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React, { useState } from 'react'
2-
import { ListRenderItem, View } from 'react-native'
2+
import { InteractionManager, ListRenderItem, Platform, View } from 'react-native'
33
import Carousel, { Pagination } from 'react-native-snap-carousel'
44

5+
import { useAsyncEffect } from '../../hooks/useAsyncEffect'
56
import { useHandler } from '../../hooks/useHandler'
67
import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
78

@@ -26,16 +27,44 @@ export function EdgeCarousel<T>(props: Props<T>): JSX.Element {
2627
const carouselRef = React.useRef<Carousel<any>>(null)
2728

2829
const [activeIndex, setActiveIndex] = useState(0)
30+
const [dataLocal, setDataLocal] = useState(data)
31+
32+
React.useEffect(() => {
33+
setDataLocal(data)
34+
}, [data])
2935

3036
const renderItem = useHandler<ListRenderItem<T>>(info => (
3137
<View style={[styles.childContainer, { width: width * 0.9, height }]}>{props.renderItem(info)}</View>
3238
))
3339

40+
/**
41+
* Carousel's FlatList bug workaround. Fixes the issue where items are
42+
* hidden until scroll actions are performed either in the carousel or on the
43+
* scene itself.
44+
*/
45+
useAsyncEffect(
46+
async () => {
47+
// HACK: With 1 item, this is the only way to force a render in iOS
48+
if (Platform.OS === 'ios' && dataLocal.length === 1) {
49+
setDataLocal([])
50+
setTimeout(() => setDataLocal(data), 50)
51+
}
52+
// The built-in hack fn works for all other cases
53+
else if (carouselRef.current != null) {
54+
await InteractionManager.runAfterInteractions(() => {
55+
carouselRef.current?.triggerRenderingHack()
56+
})
57+
}
58+
},
59+
[data],
60+
'triggerRenderingHack'
61+
)
62+
3463
return (
3564
<View style={styles.carouselContainer}>
3665
<Carousel
3766
ref={carouselRef}
38-
data={data}
67+
data={dataLocal}
3968
keyExtractor={keyExtractor}
4069
renderItem={renderItem}
4170
sliderWidth={width}
@@ -55,7 +84,7 @@ export function EdgeCarousel<T>(props: Props<T>): JSX.Element {
5584
marginTop: -theme.rem(1),
5685
marginBottom: -theme.rem(1)
5786
}}
58-
dotsLength={data.length}
87+
dotsLength={dataLocal.length}
5988
activeDotIndex={activeIndex}
6089
tappableDots={carouselRef.current != null}
6190
dotStyle={styles.dotStyle}

0 commit comments

Comments
 (0)