Description
Current behavior
We use latest versions of the FlashList package - v1.7.5 and tried downgraded version 1.7.2, but it wasn't help.
We use Animated (reanimated) Flash List in our component. When execute first render our component - all is ok, but when we go to other screen and back to screen with our component - we have error, but it caught only on android platform.
When we use FlatList from react-native - all is ok, problem reproduced only with FlashList, passed to Animated.createAnimatedComponent (from reanimated)
import type { RefObject } from 'react';
import React, { useCallback, useMemo, useRef } from 'react';
import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated';
import { FlashList, type ListRenderItem, type ListRenderItemInfo } from '@shopify/flash-list';
import { StyleSheet, View, type NativeScrollEvent, type NativeSyntheticEvent } from 'react-native';
const AnimatedFlashList = Animated.createAnimatedComponent(FlashList);
type PropsType<T> = React.ComponentPropsWithRef<typeof FlashList<T>> & {
data: T[];
isLoop?: boolean;
width?: number;
height?: number;
renderItem: ListRenderItem<T>;
};
..
function Carousel<DataType>(
props: PropsType<DataType>
) {
const carouselRef = useRef<FlashList<DataType>>(null);
const scrollOffset = useSharedValue(0);
const {
width = DEVICE_WIDTH,
height = DEVICE_HEIGHT,
isLoop = false,
data,
...otherProps
} = props;
const gappedData = useMemo(
() => (isLoop && data.length >= 3 ? [data[data.length - 1], ...data, data[0]] : data),
[isLoop, data],
);
const scrollHandler = useAnimatedScrollHandler(event => {
scrollOffset.value = event.contentOffset.x;
});
const handleMomentumScrollEnd = useCallback(
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
if (props.onMomentumScrollEnd instanceof Function) {
props.onMomentumScrollEnd(event);
}
if (!isLoop) return;
if (
event.nativeEvent.contentOffset.x === 0 ||
event.nativeEvent.contentOffset.x >= Math.floor(width * gappedData.length - width)
) {
const nextIndex = event.nativeEvent.contentOffset.x === 0 ? gappedData.length - 2 : 1;
carouselRef.current?.scrollToIndex({
animated: false,
index: nextIndex,
});
}
},
[carouselRef, gappedData.length, isLoop, props, width],
);
const listWrapperStyle = useMemo(
() =>
StyleSheet.create({ res: { alignItems: 'center', justifyContent: 'center', width, height } })
.res,
[width, height],
);
const renderItem = useCallback(
(info: ListRenderItemInfo<DataType>) => (
<SomeComponent scrollOffset={scrollOffset}>
),
[props, scrollOffset, width],
);
return (
<AnimatedFlashList
horizontal
pagingEnabled
decelerationRate="fast"
bounces={false}
overScrollMode="never"
initialScrollIndex={props.initialScrollIndex ?? 0}
scrollEventThrottle={16}
estimatedItemSize={width}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
data={gappedData}
ref={carouselRef}
onMomentumScrollEnd={handleMomentumScrollEnd}
onScroll={scrollHandler}
renderItem={renderItem}
/>
);
}
Component has been called:
<Carousel
data={resources}
renderItem={renderItem}
extraData={extraData}
isLoop={isLoop}
width={width}
height={height}
/>
Expected behavior
Expected behavior without errors and without application crashes
To Reproduce
We run application on debug or production mode. When components is mounted - we go to other screen, and when we go repeat to first screen (with our component with AnimatedFlashList) - we caught this error:
Platform:
- iOS
- Android
Environment
We also tried reproduced this case with downgraded version 1.7.2, but it was not help
"@shopify/flash-list": "1.7.5",
"react-native-reanimated": "3.15.5",
"react-native": "0.75.4",