1

I am trying to implement pagination in React Native using FlatList. I have followed the best practices, yet I am still getting the following error:

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. Object { "contentLength": 23651.732421875, "dt": 1394, "prevDt": 865, }

Here is the code:

const NewsScreen = ({ isLoading, news, fetchInitialNews, fetchMoreNews, isLoadingMore, hasMoreToFetch }) => {
 
  useEffect(() => {
    fetchInitialNews();
  }, []);

  const onEndReached = () => {
    fetchMoreNews();
  };

  return (
      <NewsList
        isLoading={isLoading}
        news={news}
        numSkeletonsToShow={LATEST_NEWS_CONSTANTS.NUM_TO_SHOW}
        contentContainerStyle={STYLES.newsListContentContainer}
        onEndReached={onEndReached}
        isLoadingMore={isLoadingMore}
        hasMoreToFetch={hasMoreToFetch}
      />
  );
};
const renderNewsItem = ({ item, index }) => (
  <NewsItem news={item} containerStyle={index !== 0 ? GLOBAL_STYLES.cardMargin : null} />
);

const NewsList = ({
  isLoading,
  news = [],
  isLoadingMore,
  contentContainerStyle = {},
  onEndReached,
  hasMoreToFetch
}) => {
  const dummySkeletonArray = Array(numSkeletonsToShow).fill("1");

  const onScrollToEnd = () => {
    if (!isLoadingMore && hasMoreToFetch) {
      onEndReached();
    }
  };

  if (isLoading) {
    return (
      //..loading indicator
    );
  }

  return (
    <FlatList
      data={news}
      keyExtractor={(n) => n.url}
      renderItem={renderNewsItem}
      showsVerticalScrollIndicator={false}
      style={GLOBAL_STYLES.flatListContentContainer}
      contentContainerStyle={contentContainerStyle}
      onEndReached={onScrollToEnd}
      onEndReachedThreshold={0.2}
      ListFooterComponent={hasMoreToFetch && <ActivityIndicator animating={isLoadingMore} />}
    />
  );
};
const areEqual = () => true;

const NewsItem = ({ news, containerStyle }) => {
  return (
    <TouchableNativeFeedback viewContainerStyle={containerStyle}>
      <Card>
      </Card>
    </TouchableNativeFeedback>
  );
};

export default memo(NewsItem, areEqual);

I have used memo and moved the renderItem outside the functional component as suggested by many other posts. Still no luck. Thank you for your help!

UPDATE:

The problem was due to conditionally rendering the ListFooterComponent (i.e. ListFooterComponent={hasMoreToFetch && <ActivityIndicator animating={isLoadingMore} />}). Changing it to ListFooterComponent={<ActivityIndicator animating={isLoadingMore} /> solves the issue. An issue has been opened by @parse (refer to the comments below) and can be found here.

1 Answers1

0

In my case, it was because of OnEndReached being called multiple times. Since you are trying to fetch next set of data from server, if onEndReached is called multiple times in a single go, it tries to call from server multiple times. I resolved by having a state to avoid multiple calls:

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

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

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

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

In some other cases, adding the below code to your Flatlist also works, where n being a small number(in my case 10).

initialNumToRender={n} 
Akshay Shenoy
  • 1,194
  • 8
  • 10
  • Hi, I have that logic implemented as you can see in the code `if (!isLoadingMore && hasMoreToFetch) { onEndReached(); }` This will only call the `onEndReached` function passed to the `NewsList` only if its not already loading more & there is more data to get. Also, for the `initialNumToRender`, what do I put if I don't exactly know how many news articles I am getting from the API? – Chandra Panta Chhetri Jan 16 '22 at 15:32
  • use prop called removeClippedSubviews and initialNumToRender when to use initialNumberToRender If There Is Pagination Where You Get 50 Data That Time I suggest using this props it'll be more affective with removeClippedSubViews – Vinit Bhavsar Jan 17 '22 at 09:19
  • onEndReachedThreshold is also extremely useful as props describe when content is half-visible to screen it will invoke onEndReached function. the setting value of this prop also helps to improve scrolling. – Vinit Bhavsar Jan 17 '22 at 09:22
  • @VinitBhavsar On Android - the device I am testing on and seeing the error, removeClippedSubviews is true by default. I have also tried the initialNumToRender prop as well. No luck. – Chandra Panta Chhetri Jan 17 '22 at 14:42
  • try to change `ListFooterComponent={hasMoreToFetch && }` to `ListFooterComponent={}` and if it works, please let me know! In my case, I have been debugging it all day and found out that when footer prop has no state, it just works fine without the warning. – parse Jan 19 '22 at 22:36
  • @parse YOU ARE A LIFE SAVER! TY, it worked! I do not see the warning anymore after removing the conditional rendering in the ListFooterComponent. However, I applied a marginTop style to the activity indicator. Even if the activity indicator hides when not animation, there is still this weird marginTop. I tried conditionally putting the style but then the warning comes back. For example `ListFooterComponent={}`. I tried using the `ListFooterComponentStyle` and doing something similar, but same issue. – Chandra Panta Chhetri Jan 20 '22 at 04:23
  • Cool, glad it worked! It sounds like a bug there. I have opened an issue on React Native github repo here https://github.com/facebook/react-native/issues/32924 please confirm that you are facing the same issue. – parse Jan 20 '22 at 21:38