React Native 虚拟化列表:您的列表很大,更新速度很慢

hmmo2u0o  于 2023-03-03  发布在  React
关注(0)|答案(9)|浏览(308)

我使用平面列表处理大量的项目。我从Expo XDE得到以下警告。
虚拟化列表:您的列表很大,更新速度很慢-请确保renderItem函数呈现的组件遵循React性能最佳实践,如PureComponent、shouldComponentUpdate等。{" dt ":13861," prevDt ":1498372326027," contentLength ":6624}
我对我的FlatList使用了一些优化方法,例如PureComponent,但我仍然收到此警报。在我描述我的优化之前,您能否告诉我,即使FlatList经过优化,此警报是否始终出现?或者它可能表示实际的性能问题?我这样问是因为我的FlatList的性能很好。

vql8enpb

vql8enpb1#

添加此 prop :

initialNumToRender={n}

为我工作(X1MON1X是一个相当短的数量,例如5)。

sq1bmfud

sq1bmfud2#

我发现了为什么会出现这个bug。主要的问题是,当你的onEndReached事件发生时,我确信你正在从服务器加载一些东西,这意味着,你需要等到你从服务器加载完成,然后你才能调用onEndReached事件。
但是在你的例子中,有多次调用onEndReached事件,所以当它发生时,你的应用程序一次又一次地试图从服务器加载数据。
好了,如何解决这个问题:你需要创建一个新的状态,例如这是通过分页实现无限滚动。

const [loader, setLoader] = useState<boolean>(false);

const onEndReached = (page) => {
  if (next && !loader) {
    setPage(page + 1)
  }
}

const loadData = async () => {
  setLoader(true);
  const resp = await getData();
  setLoader(false);
}

<FlatList ...someprops onEndReached={onEndReached} />
icnyk63a

icnyk63a3#

除了给出的所有答案之外,您还可以尝试将removeClippedSubviews设置为true

<FlatList
  removeClippedSubviews

  // ...other props
/>

通过启用removeClippedSubviews,当一个项目从视图中消失时,内存会被释放。当你有一个长而复杂的列表(即卡片列表)时,每张卡片的DOM会变得相当大,所以最好在它不可见时释放内存。
此外,如果与useCallback()而不是useMemo()合并,则在“数据”更改时会释放更多内存

const renderItem = useCallback(originalRenderItem, [data])

useMemo()方法将根据值进行记忆,但是当数据更改时,它应该真正释放自己。通过执行useCallback(),您将获得使用“函数作为参数”的好处,因此您不需要

const renderItem = useCallback(({item, index} 
  => originalRenderItem({item, index}), [data])

从而使它看起来像一个 Package 函数,而不需要为下一个人做太多的阅读。
做这两件事:

  • 防止调用最近更新的组件的可能开销很大的render()函数。
  • 减少不可见组件使用的内存
  • 如果data更改较快,则释放存储的数据。
dluptydi

dluptydi4#

另外,确保,你没有用ScrollList封装FlatList。对我来说,这是偶然出现的,因为我使用了本地基,没有注意到,他们的组件<Content>替换了ScrollList。
有关更多信息,请参阅此处:https://stackoverflow.com/a/54512633/1256697

qni6mghb

qni6mghb5#

这是对您和性能最好的答案...

import {FlashList} from "@shopify/flash-list";

 <View

    style={{
        width: WIDTH - 40,
        height:HEIGHT,
        marginTop: 16,
        // borderWidth: 1,
        // borderColor: 'red'
    }}>
    <FlashList
        viewabilityConfig={{
            waitForInteraction: true,
            itemVisiblePercentThreshold: 50,
            minimumViewTime: 1000,
        }}
        estimatedItemSize={200}
        contentContainerStyle={{
            paddingBottom: 150,
            //   transform: [{scaleX: -1}],
        }}
        data={allComments}
        showsVerticalScrollIndicator={false}
        keyExtractor={(item, index) => {
            return index;
        }}
        renderItem={({item, index}) => {
            return (
                <AllComentsComponent
                    backgroundColor={item?.status === "publish" ? Colors.dark_green : Colors.redDark}
                    color={item?.status === "publish" ? Colors.green_darker : Colors.DustRed}
                    borderColor={item?.status === "publish" ? Colors.dark_green : Colors.arghavani}
                    title={item?.ContentInfo?.title}
                    status={item.statusFa}
                    id={index}
                    date={item?.createdAt}
                    description={item?.description}
                    onPress={() => {
                    }}
                    // uri={{
                    //     uri: item?.ContentInfo?.contentPoster
                    //         ? item?.ContentInfo?.contentPoster
                    //         : `https://reactnative-examples.com/wp-content/uploads/2021/10/cosmos.jpg`,
                    // }}
                />
            )
        }}
        onEndReached={loadMoreData}
        onEndReachedThreshold={0.1}
        ListFooterComponent={renderFooter}
    />
</View>
5cnsuln7

5cnsuln76#

在导出renderItem组件时添加备注
从“React”导入React,{备忘录};. . .您的代码. . .导出默认备忘录(您的组件名称);

c9x0cxw0

c9x0cxw07#

我以前看到过此错误。优化代码后,我不再看到此错误。我通过添加console.log解决了此问题()呈现语句()创建平面列表的组件的函数,以及呈现List中每一项的函数。我注意到,以前每当页面上的任何组件发生状态更改时,我的代码都会重新呈现整个FlatList及其所有项(即使是一个与FlatList无关的组件)。我通过将各种组件转换为PureComponents修复了这个问题。下面是我的FlatList声明:

<FlatList
    ref={(ref) => { this.flatListRef = ref; }}
    data={allPosts}
    initialNumToRender={7}
    renderItem={({ item }) =>
      <Post postJson={item} isGroupAdmin={isGroupAdmin} user={user} />
    }
  />

注意,我返回的是<Post />,它是一个纯组件:

import React, { PureComponent } from 'react';
class Post extends PureComponent {

  render() { ... }
}

这确保了FlatList只在post改变时才重新呈现a。当我之前传递一个普通函数给renderItem时,也就是说,一个函数可以做如下的事情:

return (
  <View>
  ...
  </View>
);

我注意到FlatList会在任何项目发生更改时重新呈现所有项目。现在,通过使用PureComponent,FlatList只呈现添加到列表中的新项目(如果列表已经显示)。
第一次呈现整个列表仍然需要相对较长的时间。但是,initialNumToRender可以确保屏幕几乎在瞬间被填满(而其余的项在后台呈现)。更重要的是,在初始呈现之后,FlatList一次只需要呈现一项(更改的项)。
我发现这个帖子很有帮助)。
我刚刚意识到这也可以解释为here

gojuced7

gojuced78#

我注意到这个问题的答案并没有为那些使用函数组件和钩子的人提供解决方案。我遇到了这个问题,我能够通过使用钩子"useMemo()"来摆脱它。

<FlatList
                keyExtractor={keyExtractor}
                data={productsState.products}
                renderItem={renderItem}
            />
const renderItem = ({ item }) => (
            <ListItem
                title={item.ProductName}
                subtitle={(item.ProductQuantity) + " " + (item.QuantityType !== 
                null ? item.QuantityType : " ") }
                bottomDivider
                topDivider
                chevron
                checkmark={checkMark}
                onLongPress={() => setCheckMark(!checkMark)}
                rightSubtitle={(item.Currency !== null ? item.Currency: " " ) + 
                " " + (item.productCost !== null ? item.productCost: " " )}
                rightSubtitleStyle={{ marginTop: -20 }}
                badge={{ value: item.sellingPrice, textStyle: { color: 'orange' }, containerStyle: { marginTop: -20 } }}
            />
        )

renderItem函数是一个昂贵的计算,因为它需要渲染一个很长的列表。

const memoizedValue = useMemo(() => renderItem, [productsState.product]);

<FlatList
                keyExtractor={keyExtractor}
                data={productsState.products}
                renderItem={memoizedValue}
            />
const renderItem = ({ item }) => (
        <ListItem
            title={item.ProductName}
            subtitle={(item.ProductQuantity) + " " + (item.QuantityType !== 
            null ? item.QuantityType : " ") }
            bottomDivider
            topDivider
            chevron
            checkmark={checkMark}
            onLongPress={() => setCheckMark(!checkMark)}
            rightSubtitle={(item.Currency !== null ? item.Currency: " " ) + 
            " " + (item.productCost !== null ? item.productCost: " " )}
            rightSubtitleStyle={{ marginTop: -20 }}
            badge={{ value: item.sellingPrice, textStyle: { color: 'orange' }, containerStyle: { marginTop: -20 } }}
        />
    )

不要忘记从react导入useMemo,这样才能使它工作。
祝你好运!

cbjzeqam

cbjzeqam9#

如果您使用的是函数组件,那么将该组件 Package 在memo中是避免不必要呈现的好方法,而不必经历将函数组件转换为纯类组件的麻烦。
请遵循以下示例:
在父组件中:

import React from 'react';
import {FlatList} from 'react-native';
import PostCard from './PostCard';

export const NewsFeeds = props => {
      return (
        <FlatList
          data={data}
          initialNumToRender={4}
          refreshing={loading}
          renderItem={_renderitem}
        />
      );
    };

const _renderitem = ({item}) => <PostCard item={item} />;

在子组件中

import React, {memo} from 'react';
import {View} from 'react-native';

const PostCard = (props) => {
        return (
            <View>
    
            </View>
        );
    };
    
 export default memo(PostCard);

如果使用的是类组件,请通过在类定义中扩展React. PureComponent来确保组件是纯组件

class NewsFeeds extends React.PureComponent {
  render() {
    return (
      <FlatList
          data={data}
          initialNumToRender={4}
          refreshing={loading}
          renderItem={_renderitem}
      />
    )
  }
}

相关问题