116

I use FlatList with large number of items. I get following alert from Expo XDE.

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. {"dt":13861,"prevDt":1498372326027,"contentLength":6624}

I used some optimization approaches to my FlatList for example PureComponent, but I still get this alert. Before I will describe my optimizations, could you tell me if this alert appears always even though FlatList is optimized? Or maybe it indicated actual issues with performance? I ask because performance of my FlatList is good.

Hemant N. Karmur
  • 840
  • 1
  • 7
  • 21
Przemek Piechota
  • 1,513
  • 2
  • 12
  • 19

11 Answers11

100

I was previously seeing this error. After optimizing my code, I no longer see it. I figured out the problem by adding console.log() statement to the render() function of the Component that creates the FlatList, and the function that renders each item in the List. I noticed that my code was previously re-rendering the entire FlatList and all its items whenever there's a state change to any component on that page (even a component that's not related to the FlatList). I fixed this by converting various components to PureComponents. Here's what my FlatList declaration looks like:

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

Notice that I'm returning <Post /> which is a pure component:

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

  render() { ... }
}

This ensures that the FlatList re-renders a only if the post changes. When I was previously passing a normal function to renderItem i.e., a function that does something like this:

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

I noticed that the FlatList was re-rendering all items whenever any item changed. Now, by using a PureComponent, the FlatList only renders the new item added to the list (if the list is already being displayed).

It still takes relative long to render the entire list the first time. However, initialNumToRender ensures that the screen is filled up pretty much instantaneously (while the remain items get rendered in the background). And more importantly, after that initial rendering, the FlatList only ever has to render one item at a time (the item that changes).

I found this post very helpful).

I've just realized this is also explained here

Kes115
  • 2,070
  • 2
  • 21
  • 37
  • 2
    Also helpful: http://blog.flaviocaetano.com/post/tips-to-avoid-rerendering-react-native-components/ – rolznz Dec 02 '18 at 05:16
  • 2
    I am not sure if my issue is this – Kasra May 11 '20 at 19:16
  • 6
    I'm using a PureComponent but it doesn't hide the warning for me, still get " You have a large list that is slow to update" warning – Khaled Boussoffara May 26 '20 at 11:25
  • 1
    is it possible to fixt this in functional component – aditya kumar Aug 07 '20 at 08:09
  • 2
    in functional component you need to wrap your list item component with React.memo. You also need of courde to make sure only premitive props are being sent to child. If you also send functions/object/arrays etc. them you need to memoize those using useMemo or useCallback. – D10S Oct 18 '20 at 15:44
29

If you are using a functional component, wrapping the component in memo is a good way to prevent unnecessary renders without going through the hassle of converting a functional component to a pure class component. This post explains it more

follow this example:

In the parent component:

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} />;

In the child component

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

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

If you are using a class component, make sure your component is a pure component by extending React. PureComponent in your class definition

class NewsFeeds extends React.PureComponent {
  render() {
    return (
      <FlatList
          data={data}
          initialNumToRender={4}
          refreshing={loading}
          renderItem={_renderitem}
      />
    )
  }
}
Azeezat Raheem
  • 531
  • 6
  • 14
  • 1
    Thanks so much for this. The memo trick with the functional components worked well for me. I always avoid class components unless a plugin requires it. – Fiddle Freak Jun 06 '23 at 14:17
23

I noticed that the answer to this question dosen't proffer solution for those using functional component and hooks. I encountered this problem and i was able to get rid of it by using the hook "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 } }}
            />
        )

The renderItem function is an expensive computation, because it a long list to render. Instead I memoize it as follows

            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 } }}
        />
    )

Don't forget to import useMemo from react, inorder to make this work.

Good Luck!

olawalejuwonm
  • 1,315
  • 11
  • 17
15

Adding this prop :

initialNumToRender={n} 

worked for me (n being a considerably short amount, for example 5).

Reza Rahemtola
  • 1,182
  • 7
  • 16
  • 30
Samir Diaz
  • 309
  • 2
  • 6
  • 4
    I recommend using this solution before jumping to the other complicated ones in the other answers. Thanks – parse Dec 27 '21 at 22:09
6

I figured it out, why this bug is happened. The main problem is, when your onEndReached event is happened, im sure you are loading something from server, which means, you need to wait until your loading is finished from server, so after that you can call onEndReached event.

But in your case there is multilple calling of onEndReached event. So when it happens, your application was trying to load datas from server again and again.

Ok, how to solve this problem: you need to create new state, for example this is realization of infinite scrolling by pagination.

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} />
temirtator
  • 116
  • 1
  • 2
  • 8
6

On top of all the answers given, you can also try setting removeClippedSubviews to true.

<FlatList
  removeClippedSubviews

  // ...other props
/>

By enabling removeClippedSubviews the memory is freed up when an item disappears from the view. When you have a long and complex list (i.e. a list of cards) the DOM of each card can get pretty large so it's best to free up the memory when it's not visible.

In addition if you combine with useCallback() rather than useMemo() you free up a bit more memory when your "data" changes

const renderItem = useCallback(originalRenderItem, [data])

the useMemo() approach will memoize based on the value, but it should really free itself up when the data changes. By doing useCallback() you're getting the benefit of using the "function as a parameter" so you don't need to

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

Thus making it look like a wrapped function without as much reading for the next person.

Doing this two:

  • prevents the calling the potentially expensive render() function of recently updated components.
  • reduces memory used by invisible components
  • frees up the memoized data if data changes sooner.
Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265
Chan Jing Hong
  • 2,251
  • 4
  • 22
  • 41
  • Can You provide some Info about it? – Oliver D Mar 17 '21 at 13:44
  • 1
    Let me know if I missed something but adding data to dependencies of the `renderItem` doesn't make much sense and causes harm. Only time you would want to mutate `renderItem` is when you want to use multiple variations of render function for the same list. – İbrahim Duran Dec 01 '21 at 11:24
3

Also make sure, you don't encapsulate FlatList with ScrollList. For me it accidentally appears, because I used native-base, and didn't noticed, that their Component <Content> replace ScrollList.

For more Information see here: https://stackoverflow.com/a/54512633/1256697

Kampai
  • 22,848
  • 21
  • 95
  • 95
suther
  • 12,600
  • 4
  • 62
  • 99
0

There is currently a bug on Android for <FlatList /> when inverted is true which destroys performance, particularly on more recent Android versions: https://github.com/facebook/react-native/issues/30034.

The ugly workaround (see the thread) involves patching React Native, using scaleY: -1, and inability to use RefreshControl, but there's unfortunately no real alternative.

Stephen Saucier
  • 1,925
  • 17
  • 20
0

In case you are using functional components you can easily solve it using React.memo

import React from "react"
const Component= React.memo((props) => {
  // ... Existing component code ...
})

export default Component
-1

here is the best answer for you and for performance...

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>

cigien
  • 57,834
  • 11
  • 73
  • 112
-3

add memo to your renderItem component when export it

import React,{memo} from "react"; . . . your code . . . export default memo(your component name);

eyad h
  • 1
  • 2
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 28 '22 at 14:06