react-native icon indicating copy to clipboard operation
react-native copied to clipboard

Inverted SectionList/Flatlist/Flashlist has frame drop

Open devoren opened this issue 2 years ago • 15 comments

Description

I want to use a flatlist or flashlist to show a inverted list. But if I use inverted prop on android it works very slow. Default Flatlist: drops 5-10 FPS in dev mode (UI 60FPS) Inverted Flatlist: drops 30-40 FPS in dev mode (UI also 20-40FPS) Default: default.webm

Inverted: inverted.webm

Version

0.71.1

Output of npx react-native info

System: OS: Windows 10 10.0.19044 CPU: (8) x64 Intel(R) Core(TM) i5-10300H CPU @ 2.50GHz Memory: 3.17 GB / 15.84 GB Binaries: Node: 18.12.0 - C:\Program Files\nodejs\node.EXE Yarn: 1.22.19 - ~\AppData\Roaming\npm\yarn.CMD npm: 9.2.0 - C:\Program Files\nodejs\npm.CMD Watchman: Not Found SDKs: Android SDK: Not Found Windows SDK: Not Found IDEs: Android Studio: AI-221.6008.13.2211.9477386 Visual Studio: 16.11.31624.102 (Visual Studio Enterprise�2019) Languages: Java: 11.0.13 - /c/Program Files/Common Files/Oracle/Java/javapath/javac npmPackages: @react-native-community/cli: Not Found react: 18.2.0 => 18.2.0 react-native: 0.71.1 => 0.71.1 react-native-windows: Not Found npmGlobalPackages: react-native: Not Found

Steps to reproduce

  1. Init new RN app
  2. Add Flat list
  3. Set inverted prop to true

Snack, code example, screenshot, or link to a repository

 const renderItem: ListRenderItem<IMessage> = ({ item, index }) => {
	return (
		<MessageItem id={item.id} message={item.message} date={item.date} />
	)};

 const keyExtractor = ({ id, date }: IMessage) => `${id}_${date}`;

 return (
	<SafeAreaView style={styles.container}>
		<FlatList
			ref={list}
			data={messages}
			keyExtractor={keyExtractor}
			renderItem={renderItem}
			scrollEventThrottle={16}
			contentContainerStyle={{
				paddingVertical: SCALE.XS,
			}}
			showsVerticalScrollIndicator={false}
		/>
	</SafeAreaView>
  );

devoren avatar Jan 26 '23 18:01 devoren

@devoren Hi!

Sorry for the English... let's go!

Use the "initialNumToRender" property to render only the first items in the list, while the other items are rendered as the user scrolls. This can help reduce the number of initially rendered elements and therefore improve performance. Use the "windowSize" property to increase the number of items rendered in each window. This can help reduce the number of elements rendered as the user scrolls, which can also improve performance. Checks that your data source is updating correctly, preventing unnecessary updates Checks if the keyExtractor is being used correctly, avoiding unnecessary update If you're still having performance issues, you might consider using a third-party lib like react-native-largelist to improve performance.

cl3i550n avatar Jan 26 '23 19:01 cl3i550n

@cl3i550n Thanks for the quick response! I used these props to improve performance, but it didn't help much. As you said about 3rd party libraries, I'm trying to use flashlist but it also has lags but their documentation says to measure in release mod but don't think there will be big changes. So I tried scaleY: -1 instead of inverted prop but no change. I'm going to try react-native-large-list, if it helps I'll let you know

devoren avatar Jan 26 '23 19:01 devoren

It's worked

diff --git a/node_modules/react-native/Libraries/Lists/VirtualizedList.js b/node_modules/react-native/Libraries/Lists/VirtualizedList.js
index 6de43b7..4baf72c 100644
--- a/node_modules/react-native/Libraries/Lists/VirtualizedList.js
+++ b/node_modules/react-native/Libraries/Lists/VirtualizedList.js
@@ -26,6 +26,8 @@ import {
   computeWindowedRenderLimits,
 } from './VirtualizeUtils';
 
+const Platform = require('../Utilities/Platform');
+
 import * as React from 'react';
 import type {ScrollResponderType} from '../Components/ScrollView/ScrollView';
 import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
@@ -2108,9 +2110,10 @@ function describeNestedLists(childList: {
 }
 
 const styles = StyleSheet.create({
-  verticallyInverted: {
-    transform: [{scaleY: -1}],
-  },
+  verticallyInverted:
+      Platform.OS === 'android'
+          ? {scaleY: -1}
+          : {transform:  [{scaleY: -1}],},
   horizontallyInverted: {
     transform: [{scaleX: -1}],
   },

decisionnguyen avatar Jan 27 '23 04:01 decisionnguyen

@decisionnguyen I don't know why, but it doesn't work for me, if I add this condition, then the inverted prop doesn't work at all :(

devoren avatar Jan 27 '23 05:01 devoren

@cl3i550n It doesn't matter which list I use, the UI drops at 30fps, maybe the problem is with react native and not the list?

devoren avatar Jan 27 '23 05:01 devoren

@cl3i550n It doesn't matter which list I use, the UI drops at 30fps, maybe the problem is with react native and not the list?

try other device... or change react native version, try disabling hermes... I can't reproduce the problem, for me it works normal without frame drop. Try using your cell phone as an emulator, for a real simulation of the app...

cl3i550n avatar Jan 27 '23 10:01 cl3i550n

@cl3i550n What version of RN are you using?

devoren avatar Jan 27 '23 12:01 devoren

@cl3i550n What version of RN are you using?

0.71.1

cl3i550n avatar Jan 27 '23 12:01 cl3i550n

@cl3i550n I use a flash list on native stack screen, maybe because of that?

devoren avatar Jan 27 '23 12:01 devoren

const renderItem: ListRenderItem<IMessage> = ({ item, index }) => { return ( <MessageItem id={item.id} message={item.message} date={item.date} /> )};

const keyExtractor = ({ id, date }: IMessage) => ${id}_${date};

return ( <SafeAreaView style={styles.container}> <FlatList ref={list} data={messages} keyExtractor={keyExtractor} renderItem={renderItem} scrollEventThrottle={16} contentContainerStyle={{ paddingVertical: SCALE.XS, }} showsVerticalScrollIndicator={false} /> </SafeAreaView> );

Use the FlatList's initialNumToRender attribute. This attribute specifies the number of items to be rendered at first, which can help improve performance when loading large datasets.

Use the FlatList's windowSize attribute. This attribute specifies the number of items to keep in memory for each side of the screen. This can help reduce the amount of items that need to be rendered and recreated when the user scrolls down the list.

Use the FlatList's removeClippedSubviews attribute. This attribute allows you to disable rendering of items that are off-screen, which can help improve performance when scrolling through the list.

Use the FlatList's onEndReachedThreshold attribute. This attribute specifies the scroll distance in pixels before the end of the list when the onEndReached event fires. This can help prevent extra items from being triggered unnecessarily.

Use the FlatList's maxToRenderPerBatch attribute. This attribute specifies the maximum number of items that will be rendered in a single batch. This can help reduce the number of items that need to be rendered at the same time.

Use the FlatList's updateCellsBatchingPeriod attribute. This attribute specifies the amount of time, in milliseconds, that cell updates will be wrapped before being sent to the rendering process. This can help reduce the number of cell updates that need to be sent at the same time.

Use the FlatList's extraData attribute. This attribute is used to force the FlatList to re-render all items when the data changes.

Use the react-native-performance-monitor library to monitor your application's performance and identify performance bottlenecks.

Here's an example of how you can reverse the order of items in your FlatList without using the inverted property:

const renderItem: ListRenderItem<IMessage> = ({ item, index }) => {
  return (
    <MessageItem id={item.id} message={item.message} date={item.date} />
  )
};

const keyExtractor = ({ id, date }: IMessage) => `${id}_${date}`;

return (
  <SafeAreaView style={styles.container}>
    <FlatList
      ref={list}
      data={messages.slice().reverse()}
      keyExtractor={keyExtractor}
      renderItem={renderItem}
      scrollEventThrottle={16}
      contentContainerStyle={{
        paddingVertical: SCALE.XS,
      }}
      showsVerticalScrollIndicator={false}
    />
  </SafeAreaView>
);

Instead of using the inverted property, we are using the slice() and reverse() method to create an inverted copy of the data array and pass it to the FlatList. This should resolve the performance issue as the list component no longer needs to reverse the order of items on rendering.

Sorry bad english!

cl3i550n avatar Jan 27 '23 12:01 cl3i550n

@cl3i550n Thanks for the advice!!! I need inverted prop to invert scroll position, I will try all suggestions

devoren avatar Jan 27 '23 12:01 devoren

After a lot of reading and experimenting, style={{transform: [{rotate: '180deg'}]}} seems to be the best workaround for the time being.

IshmamR avatar Feb 04 '23 08:02 IshmamR

@IshmamR Thank you, i will try

devoren avatar Feb 04 '23 10:02 devoren

After a lot of reading and experimenting, style={{transform: [{rotate: '180deg'}]}} seems to be the best workaround for the time being.

Worked! But the scrollbar went to the left.

glundgrenm avatar Feb 16 '23 17:02 glundgrenm

possible duplicate of #35350

brsaylor2 avatar Mar 09 '23 00:03 brsaylor2

Closing as duplicate of https://github.com/facebook/react-native/issues/35350 and https://github.com/facebook/react-native/issues/30034 - I'm trying to reduce the number of different issues people are reporting this performance problem with Inverted Flatlist (and especially on Android 13). Please add comments to those issues instead.

kelset avatar May 09 '23 10:05 kelset