Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some performance issues when combining FlatList, react-native-gesture-handler, and react-native-reanimated #2856

Closed
yinminqian opened this issue Apr 9, 2024 · 3 comments
Labels
Close when stale The issue will be closed automatically if it remains inactive Platform: Android This issue is specific to Android Platform: MacOS Repro provided A reproduction with a snack or repo is provided

Comments

@yinminqian
Copy link

Description

I am going to replicate the apple store animation effect, there is a zoom in and zoom out effect when sliding the interface and clicking to each View, and a long press can keep the zoom out, I wrote a minimal reproduction of the example, but on the real machine and emulator, as long as the animation and flatlist sliding are running at the same time, it is just a sliding lag, is there something that I am doing wrong? What should I do to improve it? My minimal re-code has been written below

2024-04-04.15.40.00.mp4

Steps to reproduce

import { Text, SafeAreaView } from "react-native";
import { Gesture, GestureDetector, FlatList } from "react-native-gesture-handler";
import Animated, {
  Easing,
  useSharedValue,
  withTiming,
} from "react-native-reanimated";

const data = new Array(50).fill("1");
export const LoginScreen = () => {
  return <SafeAreaView style={{ flex: 1 }}>
    <FlatList data={data}
              style={{ flex: 1 }}
              renderItem={({ item, index }) => <ItemTap index={index} />}
    />
  </SafeAreaView>;
};
const ItemTap = ({ data, index }) => {

  const scale = useSharedValue(1);
  const gesture = Gesture.LongPress()
    .maxDistance(1)
    .minDuration(1000 * 60 * 30)
    .onBegin((e) => {
      "worklet";
      scale.value = withTiming(0.95, {
        duration: 300,
        easing: Easing.inOut(Easing.quad),
      });
    })
    .onEnd((e) => {
      console.log("onEnd==>", e);
    })
    .onFinalize((e) => {
      "worklet";
      scale.value = withTiming(1, {
        duration: 100,
        easing: Easing.inOut(Easing.quad),
      });
    });
  return <GestureDetector gesture={gesture}>
    <Animated.View style={{
      height: 200,
      borderRadius: 10,
      borderWidth: 1,
      borderColor: "red",
      marginBottom: 20,
      marginHorizontal: 20,
      alignItems: "center",
      justifyContent: "center",
      transform: [{ scale }],
    }}>
      <Text>{index}</Text>
    </Animated.View>
  </GestureDetector>;
};

Snack or a link to a repository

https://github.com/yinminqian/Troubleshooting

Gesture Handler version

2.15.0

React Native version

0.73.6

Platforms

Android, MacOS

JavaScript runtime

Hermes

Workflow

None

Architecture

None

Build type

None

Device

None

Device model

iphone 11 ,xiaomi10

Acknowledgements

Yes

@github-actions github-actions bot added Platform: Android This issue is specific to Android Platform: MacOS Repro provided A reproduction with a snack or repo is provided labels Apr 9, 2024
@yinminqian
Copy link
Author

2024-04-04.15.54.11.mp4

Guys,to add to this, this is the desired effect, the sliding is smooth, and tapping on each block will zoom in and out

@latekvo
Copy link
Contributor

latekvo commented Jul 29, 2024

Hi @yinminqian,
thank you for your report.

It looks like the issue lies in lack of optimisation to the Gesture Handler's FlatList as compared to React Native's one.
As far as I see, it is not related to any parts of react-native-reanimated and any parts of react-native-gesture-handler other than the FlatList component.

^^^ Removing that part of the comment for now because some further testing has led me to different discoveries.

Right now, you can work around this issue by wrapping your ItemTap component with React's memo function.

I'll keep you updated on the state of this issue and will try to resolve it as soon as possible.

@latekvo
Copy link
Contributor

latekvo commented Jul 29, 2024

Hi @yinminqian.

After some more testing and some discussion with the team I'm inclined to believe the issue you're describing is not related to any of our tools or libraries.

Of course, please feel free to keep experimenting with this setup as there could be some things I might have missed.

As I previously mentioned, you can fix this issue by memoizing your ItemTap component with React.memo, which will prevent excess re-renders from occurring.


Code used for testing:

import { memo } from 'react';
import React from 'react-native';
import {
  Text,
  SafeAreaView,
  StyleSheet,
  View,
  FlatList as RNFlatList,
} from 'react-native';
import { FlatList as GHFlatList } from 'react-native-gesture-handler';

const ItemTap = ({ index }: { index: number }) => {
  console.log('rendering', index);
  return (
    <View style={styles.staticBox}>
      <Text>{index}</Text>
    </View>
  );
};

const MemoizedItemTap = memo(ItemTap);

const data = new Array(50).fill('1');

export default function FlatListExample() {
  return (
    <SafeAreaView style={styles.fill}>
      <Text style={styles.topHeader}>Raw</Text>
      <View style={styles.container}>
        <View style={styles.fill}>
          <Text style={styles.header}>GH FlatList</Text>
          <GHFlatList
            data={data}
            style={styles.fill}
            renderItem={({ index }) => <ItemTap index={index} />}
          />
        </View>
        <View style={styles.fill}>
          <Text style={styles.header}>RN FlatList</Text>
          <RNFlatList
            data={data}
            style={styles.fill}
            renderItem={({ index }) => <ItemTap index={index} />}
          />
        </View>
      </View>
      <Text style={styles.topHeader}>Memoized</Text>
      <View style={styles.container}>
        <View style={styles.fill}>
          <Text style={styles.header}>GH FlatList</Text>
          <GHFlatList
            data={data}
            style={styles.fill}
            renderItem={({ index }) => <MemoizedItemTap index={index} />}
          />
        </View>
        <View style={styles.fill}>
          <Text style={styles.header}>RN FlatList</Text>
          <RNFlatList
            data={data}
            style={styles.fill}
            renderItem={({ index }) => <MemoizedItemTap index={index} />}
          />
        </View>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  topHeader: {
    fontSize: 24,
    fontWeight: 'bold',
    textAlign: 'center',
    margin: 5,
    borderColor: 'red',
    borderWidth: StyleSheet.hairlineWidth,
  },
  header: {
    fontSize: 20,
    fontWeight: 'bold',
    textAlign: 'center',
    margin: 5,
  },
  fill: {
    flex: 1,
  },
  container: {
    flex: 1,
    flexDirection: 'row',
  },
  staticBox: {
    height: 200,
    borderRadius: 10,
    borderWidth: 1,
    borderColor: 'red',
    marginBottom: 20,
    marginHorizontal: 20,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Results of testing:

Screen.Recording.2024-07-29.at.14.22.20.mov

@latekvo latekvo added the Close when stale The issue will be closed automatically if it remains inactive label Jul 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Close when stale The issue will be closed automatically if it remains inactive Platform: Android This issue is specific to Android Platform: MacOS Repro provided A reproduction with a snack or repo is provided
Projects
None yet
Development

No branches or pull requests

2 participants