He tenido muchos problemas para evitar recibir la advertencia "Lista virtualizada: tiene una lista grande que tarda en actualizarse" al usar un componente <FlatList> con React-Native.
Ya investigué mucho y busqué en Google-Fu para probar una solución, pero ninguna de las soluciones ayudó, ni siquiera la solución en este problema de GitHub .
Cosas a tener en cuenta:
shouldComponentUpdate() { return false; } en mi componente Elemento de lista, por lo que no se actualizan en absoluto.PureComponentrenderItem no se reinicializa en cada llamada. Importante : la advertencia solo parece ocurrir después de cambiar a una pestaña separada usando mi BottomTabNavigator y luego regresar y desplazarse por mi lista; pero esto es confuso porque cuando hago esto, el componente de pantalla FlatList no se vuelve a representar y tampoco los elementos de la lista. Dado que los componentes no se vuelven a renderizar cuando se navega a otra pestaña, ¿por qué sucedería este error?
Aquí está mi código exacto:
Aplicación.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", } });Dado que mis componentes y listas están optimizados y sigo recibiendo el error, estoy empezando a creer que esto puede ser un problema real con React Native, pero si alguien puede ver lo que estoy haciendo mal o alguna solución alternativa, esto sería de gran ayuda!
Hallazgos adicionales: encontré que la advertencia ya no ocurre si el componente CatalogScreen está contenido dentro de un NativeStackNavigator con una sola pantalla. Creo que esto puede indicar que se trata de un problema con el módulo BottomTabNavigator .
Por ejemplo, ya no aparece ninguna advertencia si realizo los siguientes cambios:
Aplicación.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> ); } } Con esta solución alternativa, renderizo el componente Stack Navigation en lugar del componente CatalogScreen directamente. Esto resuelve el problema, pero no entiendo por qué esto haría una diferencia. ¿React Native maneja los objetos de memoria de manera diferente en las pantallas de Stack Navigation en comparación con las pantallas de BottomTabNavigator?
Solo voy a intentar una respuesta al azar, pero en el mejor de los casos es una conjetura.
El video que ha vinculado en los comentarios a su pregunta dejó mucho más claro lo que está sucediendo, algo extraño está sucediendo allí.
Con listas normales como esas, especialmente en dispositivos móviles, desea renderizar y cargar solo los elementos que se muestran y son visibles actualmente, no desea mantener la lista completa de todos los elementos posibles en la memoria y renderizados todo el tiempo. Es por eso que usaría algo como una devolución de llamada para representar el elemento, de modo que la lista pueda invocar la devolución de llamada a medida que se desplaza y mostrar solo los elementos que necesita.
Dicho esto, aquí hay un par de cosas aleatorias que se me ocurren:
({prop1, prop2}) en lugar de props con ...props . Si desestructura los accesorios de esa manera, se creará un nuevo objeto cada vez que se cargue un elemento. Este podría ser uno de los culpables de su problema, si la lista plana invoca constantemente la devolución de llamada y crea toneladas de objetos nuevos. Esto también podría ser un problema potencial para el artículo del producto, si ve que recibió una nueva referencia de utilería, lo que significa que es un objeto diferente, entonces tendrá que hacer una comparación superficial (incluso si es un componente puro), en cientos de accesorios Eso realmente podría ralentizarlo. El resultado es que en realidad no lo volverá a renderizar, pero seguirá haciendo cientos de comparaciones superficiales de objetos, lo cual es un proceso lento. Así que solucione la desestructuración, apostaría a que algo como esto está causando el problema de rendimiento.this .Si todo lo demás falla, le sugiero que pregunte reaccionar nativo en github y, mientras tanto, intente usar una alternativa si hay una. Por otro lado, realmente no veo ningún problema de rendimiento en el video, por lo que quizás también sea un error que se pueda ignorar de manera segura.
Este es un viejo problema con FlatLists en React Native. FlatList en React Native no es conocido por su rendimiento. Sin embargo, esto podría mitigarse comprobando si hay renderizaciones innecesarias o componentes anidados dentro de FlatList y asegurándose de que su FlatList no esté anidada por un ScrollView. Pero ya indicó que no está volviendo a representar la lista cuando regresa a la pantalla y no está anidada por un ScrollView, por lo que probablemente sea el componente en sí.
Honestamente, recientemente tuve el mismo problema que tú, pero mi FlatList es más complejo que el tuyo. Cuando lo ejecuté en un dispositivo real, noté que no había tenido ningún problema, para mí esto solo fue un problema en Expo Go. He hecho mi parte de investigación, pero no he dado en el clavo, así que solo puedo ofrecer sugerencias.
Sugerencia 1
Echa un vistazo a este paquete RecyclerListView , parece muy prometedor.
sugerencia 2
También existe este paquete, pero soy un poco escéptico acerca de este, he escuchado quejas sobre el paquete React Native Optimized Flatlist . Estoy bastante seguro de que lo probé y no hizo nada para mí.
Además, echa un vistazo a estas dos páginas sobre el tema: