VirtualizedList:您有一个更新缓慢的大列表

IT技术 reactjs react-native react-native-flatlist
2021-04-07 08:38:50

我将 FlatList 用于大量项目。我从 Expo XDE 收到以下警报。

VirtualizedList:您有一个更新缓慢的大列表 - 确保您的 renderItem 函数呈现遵循 React 性能最佳实践的组件,如 PureComponent、shouldComponentUpdate 等。{"dt":13861,"prevDt":1498372326027,"contentLength": 6624}

我对我的 FlatList 使用了一些优化方法,例如 PureComponent,但我仍然收到此警报。在我描述我的优化之前,您能否告诉我即使 FlatList 已优化,此警报是否始终出现?或者它可能表明了性能的实际问题?我问是因为我的 FlatList 的性能很好。

6个回答

我以前看到过这个错误。优化我的代码后,我不再看到它。我通过将 console.log() 语句添加到创建 FlatList 的组件的 render() 函数以及呈现 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 仅在帖子更改时重新呈现。当我之前将一个普通函数传递给renderItemie 时,一个执行以下操作的函数:

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

我注意到只要有任何项目更改,FlatList 就会重新呈现所有项目。现在,通过使用 PureComponent,FlatList 仅呈现添加到列表中的新项目(如果列表已被显示)。

第一次渲染整个列表仍然需要相对较长的时间。但是,initialNumToRender确保屏幕几乎立即被填满(而其余项目在后台呈现)。更重要的是,在初始渲染之后,FlatList 一次只需要渲染一个项目(更改的项目)。

我发现这篇文章很有帮助)。

我刚刚意识到这也解释here

我不确定我的问题是不是这个
2021-06-01 08:38:50
在功能组件中,您需要使用 React.memo 包装您的列表项组件。您还需要确保只将前置道具发送给孩子。如果您还发送函数/对象/数组等,您需要使用 useMemo 或 useCallback 记住那些。
2021-06-03 08:38:50
是否可以在功能组件中解决这个问题
2021-06-08 08:38:50
我正在使用 PureComponent,但它并没有为我隐藏警告,仍然收到“您有一个更新缓慢的大列表”警告
2021-06-09 08:38:50
2021-06-15 08:38:50

我注意到这个问题的答案并没有为那些使用功能组件和钩子的人提供解决方案。我遇到了这个问题,我能够通过使用钩子“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,以完成这项工作。

祝你好运!

仍然收到同样的警告
2021-06-06 08:38:50
可以检查一下,你有什么想法吗?
2021-06-09 08:38:50
所有的记忆项都依赖于同一个状态。这不会使每个项目在productsState.product更改时重新呈现吗?
2021-06-09 08:38:50
是的,它只会在 productsState.product 时重新渲染
2021-06-12 08:38:50
我刚回答
2021-06-17 08:38:50

我想通了,为什么会发生这个错误。主要问题是,当您的 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} />

还要确保不要用 SourceList 封装 FlatList对我来说,它偶然出现,因为我使用了 native-base,并且没有注意到,他们的 Component<Content>替换了 ScrollList。

有关更多信息,请参见此处:https : //stackoverflow.com/a/54512633/1256697

添加此props:

initialNumToRender={n} 

为我工作(n相当少,例如 5)。