facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.61k stars 24.28k forks source link

"VirtualizedList: You have a large list that is slow to update" warnings even though all components are optimized and none are re-rendering. #32680

Closed jordanarldt closed 4 months ago

jordanarldt commented 2 years ago

Description

I've been having lots of trouble trying to avoid getting the "VirtualizedList: You have a large list that is slow to update" warning when using a <FlatList> component with React-Native.

I've already done loads of research and Google-Fu to try a solution, but none of the solutions helped, not even the solution on this GitHub issue.

Things to keep in mind:

Important: The warning only seems to occur after switching to a separate tab using my BottomTabNavigator and then coming back and scrolling through my list; but this is confusing because when I do this, the FlatList screen component is not re-rendering and neither are the list items. Since the components aren't re-rendering when browsing to another tab.

Here's my exact code:

App.tsx

const AppRoutes = [
  { name: "Home", Component: HomeScreen },
  { name: "Catalog", Component: CatalogScreen },
  { name: "Cart", Component: CartScreen }
];

export const StoreApp = () => {
  return (
    <NavigationContainer>
      <StatusBar barStyle="dark-content" />
      <Tabs.Navigator>
        {AppRoutes.map((route, index) => 
          <Tabs.Screen
            key={index}
            name={route.name}
            component={route.Component}
            options={{
              headerShown: (route.name !== "Home") ?? false,
              tabBarIcon: props => <TabIcon icon={route.name} {...props} />
            }}
          />
        )}
      </Tabs.Navigator>
    </NavigationContainer>
  );
};

CatalogScreen.tsx

import React from "react";
import { FlatList, SafeAreaView, Text, View, StyleSheet } from "react-native";
import { LoadingSpinnerOverlay } from "../components/LoadingSpinnerOverlay";
import { getAllProducts, ProductListData } from "../api/catalog";

class ProductItem extends React.Component<{ item: ProductListData }> {
  shouldComponentUpdate() {
    return false;
  }

  render() {
    return (
      <View>
        {console.log(`Rendered ${this.props.item.name}-${Math.random()}`)}
        <Text style={{height: 100}}>{this.props.item.name}</Text>
      </View>
    );
  }
}

export class CatalogScreen extends React.PureComponent {
  state = {
    productData: []
  };

  componentDidMount() {
    getAllProducts()
    .then(response => {
      this.setState({ productData: response.data });
    })
    .catch(err => {
      console.log(err);
    });
  }

  private renderItem = (props: any) => <ProductItem {...props} />;
  private keyExtractor = (product: any) => `${product.id}`;
  private listItemLayout = (data: any, index: number) => ({
    length: 100,
    offset: 100 * index,
    index
  });

  render() {
    const { productData } = this.state;
    console.log("CATALOG RENDERED");

    return (
      <SafeAreaView style={styles.pageView}>
        {!productData.length ? (
          <LoadingSpinnerOverlay text="Loading products..." />
        ) : (
        <View style={{backgroundColor: "red", height: "50%"}}>
          <FlatList
            data={productData}
            removeClippedSubviews
            keyExtractor={this.keyExtractor}
            renderItem={this.renderItem}
            getItemLayout={this.listItemLayout}
          />
        </View>
        )}
      </SafeAreaView>
    );
  }
};

const styles = StyleSheet.create({
  pageView: {
    height: "100%",
    position: "relative",
  }
});

Additional Findings / Workaround: I found that the warning no longer occurs if the CatalogScreen component is contained inside of a NativeStackNavigator with a single Screen. I believe this may indicate that this is a problem with the BottomTabNavigator module.

For example, the no warning no longer occurs if I make the following changes:

App.tsx

const AppRoutes = [
  { name: "Home", Component: HomeScreen },
  { name: "Catalog", Component: CatalogPage }, // Changed CatalogScreen to CatalogPage
  { name: "Cart", Component: CartScreen }
];

CatalogScreen.tsx

const Stack = createNativeStackNavigator();

export class CatalogPage extends React.PureComponent {
  render() {
    return (
      <Stack.Navigator>
        <Stack.Screen 
          name="CatalogStack" 
          options={{ headerShown: false }}
          component={CatalogScreen}
        />
      </Stack.Navigator>
    );
  }
}

With this workaround, I'm rendering the Stack Navigation component instead of the CatalogScreen component directly. This resolves the problem, which indicates that there is an issue with React Native's BottomTabNavigation.

Version

0.66

Output of react-native info

info Fetching system and libraries information... System: OS: macOS 12.0.1 CPU: (8) arm64 Apple M1 Memory: 68.67 MB / 8.00 GB Shell: 3.2.57 - /bin/bash Binaries: Node: 16.13.0 - ~/.nvm/versions/node/v16.13.0/bin/node Yarn: Not Found npm: 8.1.0 - ~/.nvm/versions/node/v16.13.0/bin/npm Watchman: 2021.11.08.00 - /usr/local/bin/watchman Managers: CocoaPods: 1.11.2 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: DriverKit 21.0.1, iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0 Android SDK: Not Found IDEs: Android Studio: Not Found Xcode: 13.1/13A1030d - /usr/bin/xcodebuild Languages: Java: Not Found npmPackages: @react-native-community/cli: Not Found react: 17.0.2 => 17.0.2 react-native: 0.66.2 => 0.66.2 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found

Steps to reproduce

  1. Create a homepage with a BottomTabNavigator
  2. In one of the pages, create a FlatList with enough data for the page to be scrollable
  3. Optimize all components and use PureComponents to prevent re-rendering
  4. Browse away from the FlatList page on the TabNavigator
  5. Come back to the FlatList page and continue scrolling
  6. The warning will occur.

Replication video: https://drive.google.com/file/d/1kTneCRuLAYojTbcds0qvTPUfhl4B1yto/view

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

No response

github-actions[bot] commented 2 years ago
:warning: Missing Environment Information
:information_source: Your issue may be missing information about your development environment. You can obtain the missing information by running react-native info in a console.
itsramiel commented 2 years ago

same error. When navigating to another screen and then coming back and swiping through a flatlist, I get this error.

ConnorMarble commented 2 years ago

Same issue. Any luck resolving this @itsramiel @jordanarldt?

jordanarldt commented 2 years ago

@ConnorMarble no luck on my end. Ended up needing to macgyver a workaround unfortunately by ditching the react navigation tabs.

jake-stewart commented 10 months ago
 LOG  VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc. {"contentLength": 32664, "dt": 4942, "prevDt": 767}

i've noticed that the error has dt which is exactly how long i spend idle. if i sit around for 5 seconds, then interact with the list, then dt in my error message shows 5000.

i changed scrollEventThrottle from 0 to 100 and the warning disappears. guessing this warning relies on scroll events for its calculation.

github-actions[bot] commented 4 months ago

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] commented 4 months ago

This issue was closed because it has been stalled for 7 days with no activity.

Splicer97 commented 3 months ago

same issue

sdthaker commented 3 months ago

same issue

GeMiNiOranGe commented 2 weeks ago

same issue

Ozaoujal commented 1 week ago

I have the same issue. I’ve tried to optimize component, but the error still occurs.