Creating an Infinite Scroll with FlatList in React Native

Infinite scroll is a popular pattern for displaying large lists of data. It allows users to scroll through data without experiencing the performance issues that can occur when loading all data at once. In this Interview article, we’ll explore how to implement an infinite scrolling list using React Native’s FlatList component. We’ll fetch data from an API and load more items as the user scrolls to the end of the list.

Every React Native developer must understand different props that can be passed in a FlatList to make our lives easier for this Infinite Scroll.

<FlatList
    data={data}
    keyExtractor={(item) => item.id.toString()}
    renderItem={renderItem}
    onEndReached={loadMore}
    onEndReachedThreshold={0.5}
    ListFooterComponent={renderFooter}
/>

The FlatList component is used to render the data:

  • data: The data to be rendered in the list.
  • keyExtractor: A unique key for each item.
  • renderItem: A function to render each item.
  • onEndReached: A function to call when the end of the list is reached.
  • onEndReachedThreshold: Specifies how close to the end of the list the onEndReached should trigger.
  • ListFooterComponent: A component to render at the end of the list (in this case, a loading spinner).

Another important aspect of creating Infinite Scroll is fetching the data properly, and need to call this fetchData for every page.

  const fetchData = async () => {
    if (loading) return;

    setLoading(true);
    try {
      const response = await fetch(`${DATA_API_URL}?_page=${page}&_limit=10`);
      const result = await response.json();
      setData(page === 1 ? result : [...data, ...result]);
      setHasMore(result.length > 0);
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
    }
  };

Fetching Data

The fetchData function is responsible for fetching data from the API:

  1. It first checks if data is already being loaded to prevent duplicate requests.
  2. It sets the loading state to true.
  3. Data is fetched from the API using the fetch function, with pagination parameters _page and _limit.
  4. The new data is appended to the existing data if it’s not the first page.
  5. The hasMore state is updated based on the length of the fetched data.
  6. Finally, the loading state is set to false.

Implementation in React Native

import React, { useState, useEffect } from "react";
import { View, Text, FlatList, ActivityIndicator, StyleSheet } from "react-native";

const DATA_API_URL = "https://jsonplaceholder.typicode.com/posts";

const App = () => {
  const [data, setData] = useState([]);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);

  useEffect(() => {
    fetchData();
  }, [page]);

  const fetchData = async () => {
    if (loading) return;

    setLoading(true);
    try {
      const response = await fetch(`${DATA_API_URL}?_page=${page}&_limit=10`);
      const result = await response.json();
      setData(page === 1 ? result : [...data, ...result]);
      setHasMore(result.length > 0);
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
    }
  };

  const loadMore = () => {
    if (!loading && hasMore) {
      setPage(page + 1);
    }
  };

  const renderFooter = () => {
    if (!loading) return null;
    return <ActivityIndicator style={{ margin: 15 }} />;
  };
  
  const renderItem = ({item}) => {
    return (
          <View style={styles.item}>
            <Text style={styles.title}>{item.title}</Text>
            <Text>{item.body}</Text>
          </View>
      )
  }

  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        keyExtractor={(item) => item.id.toString()}
        renderItem={renderItem}
        onEndReached={loadMore}
        onEndReachedThreshold={0.5}
        ListFooterComponent={renderFooter}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    paddingTop: 20,
  },
  item: {
    backgroundColor: "#f9c2ff",
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
  },
  title: {
    fontSize: 24,
  },
});

export default App;

Loading More Data

The loadMore function increments the page number to fetch the next set of data when the end of the list is reached.

Render Each Row

the renderItem function renders for each and every item of the data.

FlatList uses the virtualization concept to render the items on the screen, because of which the app remains performant even when dealing with large amounts of data.

1 comment