'FlatList infinite loop - React Native
I'm trying to make a Flatlist with infinite scrolling in both directions.
There is already a small outline of the implementation (https://snack.expo.dev/@slam_ua/flatlist-loop), but I can't seem to do a few things:
- Seamless list. Scrolling stops when data is updated.
- Infinite scroll up not working.
- Centering the initial coordinate (00) in the center (from the picture with an example it will be clear what I mean).
Below I have shown an example of the result I want to achieve:
Solution 1:[1]
We can tweak react-native-circular-wheel-picker to achieve this.
import React, { useEffect, useRef, useState } from "react"
import {
NativeScrollEvent,
NativeSyntheticEvent,
FlatList,
Text,
View,
StyleProp,
TextStyle,
ViewStyle,
StyleSheet,
} from "react-native"
type dataType = {
value: number | string
label: number | string
}
interface WheelNumberPickerProps {
data: dataType[]
height: number
textStyle?: StyleProp<TextStyle>
selectedTextStyle?: StyleProp<TextStyle>
unselectedTextStyle?: StyleProp<TextStyle>
dividerWidth?: ViewStyle["borderBottomWidth"]
dividerColor?: ViewStyle["borderBottomColor"]
selectedValue?: number | string
onValueChange?: (value: number | string) => void
}
function WheelNumberPicker({
height = 25,
textStyle,
selectedTextStyle,
unselectedTextStyle,
dividerWidth = 1,
dividerColor,
selectedValue = 0,
onValueChange,
data = [],
}: WheelNumberPickerProps) {
const [dataArray] = useState<dataType[]>([...data, ...data, ...data])
const [value, setValue] = useState<number | string>(selectedValue)
const flatListRef = useRef<FlatList>()
const currentYOffset = useRef<number>(0)
const numberOfValue = useRef<number>(data.length)
const initialOffset = useRef<number>((data.length - 0.5) * height)
useEffect(() => {
if (!onValueChange) {
return
}
onValueChange(value)
}, [value, onValueChange])
const onScroll = ({ nativeEvent }: NativeSyntheticEvent<NativeScrollEvent>) => {
const offsetY = nativeEvent.contentOffset.y
let index = Math.ceil((offsetY % initialOffset.current) / height)
index = index < numberOfValue.current ? index : numberOfValue.current - 1
const selectedValue = data[index].value
if (value !== selectedValue) {
setValue(selectedValue)
}
if (offsetY < currentYOffset.current) {
if (offsetY <= initialOffset.current - height) {
flatListRef.current?.scrollToOffset({
offset: offsetY + height * numberOfValue.current,
animated: false,
})
currentYOffset.current = offsetY + height * numberOfValue.current
return
}
}
if (offsetY > currentYOffset.current) {
if (offsetY > initialOffset.current + height) {
flatListRef.current?.scrollToOffset({
offset: offsetY - height * numberOfValue.current,
animated: false,
})
currentYOffset.current = offsetY - height * numberOfValue.current
return
}
}
currentYOffset.current = offsetY
}
return (
<View style={{ alignItems: "center", justifyContent: "center" }}>
<View
style={{
position: "absolute",
borderTopWidth: dividerWidth,
borderBottomWidth: dividerWidth,
borderColor: dividerColor,
height,
width: height * 1.2,
}}
/>
<View style={{ width: height * 1.2, height: height * 5 }}>
<FlatList
data={dataArray}
onScroll={onScroll}
ref={flatListRef}
showsVerticalScrollIndicator={false}
snapToAlignment="center"
snapToInterval={height}
scrollEventThrottle={12}
decelerationRate="fast"
keyExtractor={(_, index) => index.toString()}
renderItem={({ item }) => {
return (
<View
style={{
width: "100%",
height,
alignItems: "center",
justifyContent: "center",
}}>
<Text style={[textStyle, selectedTextStyle]}>{item.label}</Text>
</View>
)
}}
/>
</View>
</View>
)
}
export default WheelNumberPicker
We use it as follows.
const [data] = useState(
Array(24)
.fill(0)
.map((_, index) => {
return {
value: index,
label: index < 10 ? "0" + index : index,
}
})
)
return (
<View style={{ marginTop: 250 }}>
<WheelNumberPicker height={30} data={data} />
</View>
)
The above yields to the following result.
Solution 2:[2]
You can achieve this by using simple picker libraries in React Native. The View/UI You want, you have to create components for them. you can use this library:
react-native-picker
npm i react-native-picker
import Picker from 'react-native-picker';
let data = [];
for(var i=0;i<100;i++){
data.push(i);
}
Picker.init({
pickerData: data,
selectedValue: [59],
onPickerConfirm: data => {
console.log(data);
},
onPickerCancel: data => {
console.log(data);
},
onPickerSelect: data => {
console.log(data);
}
});
Picker.show();
Or
react-native-wheel-picker
https://www.npmjs.com/package/react-native-wheel-picker
After Edit: Not Removing Above libraries, if someone needs it ever.
For infinite scroll, you can use and tweak this library: https://www.npmjs.com/package/react-native-infinite-looping-scroll
Here's the link of demo:
https://drive.google.com/uc?id=1re6VhBZ8NZIsPYvN5DMhgveA7ei87N9U
Working Demo, might be bit laggy because running on snack.
But works:

Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | David Scholz |
| Solution 2 |


