Skip to content

Swipeable significantly decrease FlashList/FlatList performance #3141

Closed

Description

@olivertylsar

Description

I noticed in my app that wrapping a render item in a Swipeable component (whether using the Legacy or Reanimated version) leads to a significant drop in FPS and a general decrease in the performance of FlashList/FlatList.

I tested this in your example code and was able to reproduce the issue.

no-swipeable.mp4
with-swipeable.mp4

Steps to reproduce

In the example app, install @shopify/flash-list (alternative code with FlatList is commented). Replace code in swipeableReanimation/index.tsx with code below and open the screen.

It happens only on Android for me, with both JS Debug mode on and off.

import { Text, StyleSheet, View, Alert, FlatList } from 'react-native';

import ReanimatedSwipeable from 'react-native-gesture-handler/ReanimatedSwipeable';
import {
  FlashList,
  ListRenderItem,
  useBenchmark,
  useFlatListBenchmark,
} from '@shopify/flash-list';

function RightAction() {
  return <Text style={styles.rightAction}>Text</Text>;
}

type Item = {
  id: string;
  title: string;
};

const generateItems = (count: number): Item[] => {
  return Array.from({ length: count }, () => {
    const randomNumber = Math.random().toString(36).substring(2, 8);
    return {
      id: randomNumber,
      title: `Title ${randomNumber}`,
    };
  });
};

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}>
        <RenderItemViewMemoed {...item} />
      </ReanimatedSwipeable>
    );
  };

  const flashListRef = useRef<FlashList<Item> | null>(null);
  useBenchmark(flashListRef, (callback) => {
    Alert.alert('result', callback.formattedString);
  });

  return (
    <FlashList
      ref={flashListRef}
      data={data}
      renderItem={renderItem}
      estimatedItemSize={50}
      keyExtractor={(item) => item.id}
    />
  );

  // Alternatively measure performance with FlatList
  // const flatList = useRef<FlatList<Item> | null>(null);
  // useFlatListBenchmark(
  //   flatList,
  //   (callback) => {
  //     Alert.alert('result', callback.formattedString);
  //   },
  //   {
  //     targetOffset: 10000,
  //   }
  // );
  //
  // return (
  //   <FlatList
  //     ref={flatList}
  //     data={data}
  //     renderItem={renderItem}
  //     keyExtractor={(item) => item.id}
  //   />
  // );
}

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',
  },
});

Snack or a link to a repository

https://snack.expo.io/

Gesture Handler version

2.20.0

React Native version

0.73.8

Platforms

Android

JavaScript runtime

Hermes

Workflow

React Native (without Expo)

Architecture

Paper (Old Architecture)

Build type

Release mode

Device

Real device

Device model

Samsung S21

Acknowledgements

Yes

Activity

  1. github-actions commented on Oct 7, 2024

    @github-actions

    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?

  2. efstathiosntonas commented on Oct 17, 2024

    @efstathiosntonas

    This smells like onLayout events pollution, maybe measure can come into play?

  3. linked a pull request that will close this issueOptimise `ReanimatedSwipeable` component #3165on Oct 18, 2024
  4. latekvo commented on Oct 25, 2024

    @latekvo
    Member

    Hi @olivertylsar,

    Could you please let me know if this PR works well for you?

    This smells like onLayout events pollution, maybe measure can come into play?

    @efstathiosntonas thank you for the insight.
    The issues were mostly caused by lack of memoization, excessive JS-UI synchronisations and SharedValues having their value props observed in the dependency arrays of useMemo hooks.
    I could be wrong, but as far as I know onLayout had nothing to do with these issues.

  5. olivertylsar commented on Oct 29, 2024

    @olivertylsar
    Author

    Hi @latekvo,

    I tried the FlashList example in the PR's branch and the results are much better now. The FPS still drops but only to something like 68 on average. I can only see a little of the blank spaces, the improvement is significant. Thanks for looking into it. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions