react-native-gesture-handler
react-native-gesture-handler copied to clipboard
ReanimatedSwipeable | slow performance scrolling
Description
FPS down when I'm using ReanimatedSwipeable from import Swipeable from 'react-native-gesture-handler/ReanimatedSwipeable' in each item Items are not been loaded until fetch from API is done, so this is not a load data issue. If I replace it for Swipeable from import Swipeable from 'react-native-gesture-handler/Swipeable' all work great
This issue is happening in Dev and Release apps and using Hermes + RN's New arch.
Note: I applied FlatList performance strategies and tested it with/without them and the issue is still happening.
Steps to reproduce
- Run app on real device (issue is most visible on Android devices).
- Turn on Perf Monitor.
- Create a List with items using FlatList
- Add Swipeable actions for each item.
- Scroll down/up list.
Snack or a link to a repository
https://github.com
Gesture Handler version
2.21.0, 2.21.1 and 2.21.2
React Native version
0.76.5
Platforms
Android, iOS
JavaScript runtime
Hermes
Workflow
React Native (without Expo)
Architecture
Fabric (New Architecture)
Build type
Release mode
Device
Real device
Device model
Huawei P30 Pro, iPhone 16 Pro Max
Acknowledgements
Yes
Hey! 👋
The issue doesn't seem to contain a minimal reproduction.
Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem?
Hi, I tested both the legacy, and the reanimated Swipeable on the main branch, using the FlashList and useBenchmark from @shopify/flash-list.
I didn't notice any performance advantage in using the legacy Swipeable over the reanimated Swipeable, both averaged 30fps on Samsung S21 Ultra.
Test code - press to uncollapse
import React, { Text, StyleSheet, View, Alert } from 'react-native';
import ReanimatedSwipeable from 'react-native-gesture-handler/ReanimatedSwipeable';
import { FlashList, ListRenderItem, useBenchmark } from '@shopify/flash-list';
import { memo, useRef } from 'react';
import {
GestureHandlerRootView,
// Swipeable as LegacySwipeable,
} from 'react-native-gesture-handler';
function RightAction() {
return <Text style={styles.rightAction}>Text</Text>;
}
type Item = {
id: string;
testID: string;
title: string;
};
const generateItems = (count: number): Item[] => {
return Array(count)
.fill(0)
.map((value, idx) => {
// render speed test
return {
id: String(idx),
testID: String(idx),
title: `Number ${idx}`,
};
// re-renders test
// return {
// id: String(idx),
// testID: String(value),
// title: `Number ${value}`,
// };
});
};
const data = generateItems(200);
const _RenderItemView = (item: Item) => (
<View style={styles.swipeable}>
<Text>{item.title}</Text>
</View>
);
const RenderItemViewMemoed = memo(_RenderItemView);
export default function Example() {
const renderItem: ListRenderItem<Item> = ({ item }) => {
return (
<ReanimatedSwipeable
renderRightActions={RightAction}
testID={item.testID}>
<RenderItemViewMemoed {...item} />
</ReanimatedSwipeable>
);
};
const flashListRef = useRef<FlashList<Item> | null>(null);
useBenchmark(flashListRef, (callback) => {
Alert.alert('result', callback.formattedString);
});
return (
<GestureHandlerRootView>
<FlashList
ref={flashListRef}
data={data}
renderItem={renderItem}
estimatedItemSize={50}
keyExtractor={(item) => item.id}
/>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
leftAction: { width: 50, height: 50, backgroundColor: 'crimson' },
rightAction: { width: 50, height: 50, backgroundColor: 'purple' },
separator: {
width: '100%',
borderTopWidth: 1,
},
swipeable: {
height: 50,
backgroundColor: 'papayawhip',
alignItems: 'center',
},
});
Does this issue still occur for you on version 2.22.0-rc.0?
If so, could you please provide a reproduction where the difference is visible?
I also encounter the slow performance on scrolling on ReanimatedSwipeable, it looks jittery when scrolled, but only when I use the methods like onSwipeableOpen or onSwipeableClose. When I remove these, it's back to normal.
<ReanimatedSwipeable //containerStyle={[style]} ref={reanimatedRef} friction={2} leftThreshold={80} rightThreshold={80} renderLeftActions={LeftAction} renderRightActions={RightAction} onSwipeableOpen={() => { console.log('jittery'); }} > {children} </ReanimatedSwipeable>
Can you please try and check this?
I second the usage of onSwipeableOpen={() => { }} leads to huge stutter.
@davelynInes have you found any workaround for using onSwipeableOpen without having an unusable app ?
I second the usage of onSwipeableOpen={() => { }} leads to huge stutter.
@davelynInes have you found any workaround for using onSwipeableOpen without having an unusable app ?
I used the old/legacy react-native-gesture-handler/Swipeable for now. I didn't have issues with it.
I confirm the new Swipeable is very slow within an Animated.FlatList.
I second the usage of onSwipeableOpen={() => { }} leads to huge stutter. @davelynInes have you found any workaround for using onSwipeableOpen without having an unusable app ?
I used the old/legacy react-native-gesture-handler/Swipeable for now. I didn't have issues with it.
What version are you using
I second the usage of onSwipeableOpen={() => { }} leads to huge stutter.
@davelynInes have you found any workaround for using onSwipeableOpen without having an unusable app ?
I used the old/legacy react-native-gesture-handler/Swipeable for now. I didn't have issues with it.
Same here... Old swipeable is so clean and fast in android
I added ReanimatedSwipeable to a SectionList and JS FPS dropped from 60 to much lower during scrolling and other animations.
I spent a few hours trying to figure out if there was something wrong with my code and optimize performance. Commenting out the wrapping <ReanimatedSwipeable> brought back buttery performance. It must be either creating a bunch of views, or running a bunch of code, because it's a serious drain on performance.
Can confirm that The drain in performance is is huge, almost doubling rendering time of each of my list item.
Hi, I'm still facing the same issue: Expo 54, React-Native 0.81.5, new architecture. FPS drops a lot when scrolling.
Just to try to bump this issue: I'm having the same problem. I tried what @davelynInes suggested about using the old/legacy react-native-gesture-handler/Swipeable and it worked.
I noticed that there are other PRs and/or issues related to this too, so it would be great if this issue could be priorised 🙏🏻 Have a good day.
So @davelynInes post (at the top of the thread) sparked my attention.
What we know is that setting up Gesture is a very expensive operation, and it’s usually recommended to useMemo the gesture. ReanimatedSwipeable tries that, but after inspecting the code I can see that onSwipeableOpen and onSwipeableClose are directly passed as dependencies to dispatchEndEvents and dispatchImmediateEvents, which are dependencies for animateRow. Long story short, that simple prop causes the whole memoization chain to break, rendering every effort to memoize the Gesture useless. That’s the ugly and tricky part about useCallback and useMemo: a single mistake is enough.
You could try to pass a stable function to onSwipeableOpen with useCallback and see if that fixes the issue. If that doesn’t work, ReanimatedSwipeable itself should be wrapped with memo on top of that. But ReanimatedSwipeable needs fully stable props to prevent tapGesture from being re-created over and over again.
I did not verify my findings, but I am pretty sure that this could be the culprit.
One more hint: if used with FlatList, which uses no recycling, using ReanimatedSwipeable is not recommended, as it will re-create the item every time, and the gesture setup is definitely expensive. Use Flashlist, or better, LegendList