VirtualizedList:您有一个很难更新的大型列表

Prz*_*ota 25 reactjs react-native react-native-flatlist

我使用具有大量项目的FlatList.我收到了来自Expo XDE的警告.

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

我在FlatList中使用了一些优化方法,例如PureComponent,但我仍然得到这个警告.在我描述我的优化之前,你能否告诉我,即使FlatList被优化,是否仍会出现此警报?或者它可能表明性能的实际问题?我问,因为我的FlatList的表现很好.

Aze*_*eem 39

如果您使用的是函数式组件,则将组件包装起来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} />;
Run Code Online (Sandbox Code Playgroud)

在子组件中

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

const PostCard = (props) => {
        return (
            <View>
    
            </View>
        );
    };
    
 export default memo(PostCard);
Run Code Online (Sandbox Code Playgroud)

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

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


Kes*_*115 25

我以前看到过这个错误.优化我的代码后,我不再看到它.我通过将console.log()语句添加到创建FlatList的Component的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} />
    }
  />
Run Code Online (Sandbox Code Playgroud)

请注意,我正在返回<Post />哪个是纯组件:

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

  render() { ... }
}
Run Code Online (Sandbox Code Playgroud)

这可确保FlatList仅在帖子更改时重新呈现.当我以前将正常函数传递给renderItemie时,执行类似这样的函数:

return (
  <View>
  ...
  </View>
);
Run Code Online (Sandbox Code Playgroud)

我注意到FlatList在任何项目发生变化时都会重新渲染所有项目.现在,通过使用PureComponent,FlatList仅呈现添加到列表中的新项目(如果列表已经显示).

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

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

我刚刚意识到这也在这里解释

  • 我正在使用 PureComponent,但它不会为我隐藏警告,仍然收到“您有一个更新缓慢的列表”警告 (3认同)
  • 也有帮助:http://blog.flaviocaetano.com/post/tips-to-avoid-rerendering-react-native-components/ (2认同)
  • 我不确定我的问题是否是这个 (2认同)
  • 在功能组件中,您需要使用 React.memo 包装列表项组件。您还需要适当的措施来确保只将预先的道具发送给孩子。如果您还发送函数/对象/数组等,您需要使用 useMemo 或 useCallback 来记住它们。 (2认同)

Sam*_*iaz 15

添加这个道具:

initialNumToRender={n} 
Run Code Online (Sandbox Code Playgroud)

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

  • 我建议在跳到其他答案中的其他复杂解决方案之前使用此解决方案。谢谢 (4认同)

ola*_*onm 12

我注意到这个问题的答案并没有为那些使用功能组件和钩子的人提供解决方案。我遇到了这个问题,我能够通过使用钩子“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 } }}
            />
        )
Run Code Online (Sandbox Code Playgroud)

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 } }}
        />
    )
Run Code Online (Sandbox Code Playgroud)

不要忘记从 react 中导入 useMemo,以完成这项工作。

祝你好运!

  • 仍然收到相同的警告 (4认同)

Cha*_*ong 8

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

<FlatList
  removeClippedSubviews

  // ...other props
/>
Run Code Online (Sandbox Code Playgroud)

通过启用,removeClippedSubviews当项目从视图中消失时会释放内存。当你有一个长而复杂的列表(即卡片列表)时,每张卡片的 DOM 可能会变得相当大,因此最好在它不可见时释放内存。

此外,如果您结合useCallback()而不是useMemo()在“数据”更改时释放更多内存

const renderItem = useCallback(originalRenderItem, [data])
Run Code Online (Sandbox Code Playgroud)

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

const renderItem = useCallback(({item, index} 
  => originalRenderItem({item, index}), [data])
Run Code Online (Sandbox Code Playgroud)

因此使它看起来像一个包装函数,而无需为下一个人提供太多阅读。

做这两个:

  • 防止调用render()最近更新的组件的可能昂贵的函数。
  • 减少不可见组件使用的内存
  • 如果更改较早,则释放已存储的数据data


tem*_*tor 6

我明白了,为什么会出现这个错误。主要问题是,当您的 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} />
Run Code Online (Sandbox Code Playgroud)