How to provide more data correctly to a Flatlist?

Asked

Viewed 811 times

0

I’m building an app on React Native using the services of Firebase and Redux. Every day, I register new Documents there and so my database gets bigger.

In my app I was giving a rescue all records of a collection of Firebase when opening a kind of screen "Feed". I knew I would eventually have to implement a code pagination. So, recently I reached ~35 records on DB and things got weird: The app didn’t always load all the records, and when it did, it didn’t allow it to touch the renderItems of FlatList.

Here is the code of mine FlatList:

<FlatList 
      ListHeaderComponent={/*irrelevante*/}
      data={this.props.locals} 
      keyExtractor={item => item.id} 
      renderItem={this.renderLocal} 
/> 

this.renderLocal

renderLocal = ({item, index}) => {
    return <Local {...item} goToLocal={() => this.goToLocal(item.id)}/>
}

Important to mention: the component Local which is rendered several times by Flatlist, is "heavy" since it contains certain styling, two images loaded through URI and two images stored locally.

So I started the process of creating a pagination, doing some things:

  1. I created the paginationIndex and inserted it into the state in a reducer with value of 15.
  2. Dei slice(0, this.props.paginationIndex) in the property array data in the FlatList. I chose to do so the expectation that my database contains something like 200 records at most, which would be light when rescuing all and entering in the Reducer.
  3. I created a button that gives a dispatch which increases by 15 o paginationIndex.
  4. I made my component Local in a PureComponent, to improve performance with FlatList

And the resulting behavior was always the same: at the beginning it loads the 15 components, when clicking on the pagination button, sometimes another 15 appear but everything hangs (as described in the second paragraph). I tried to make changes to understand the limits of that. Searching 15 initially, with a pagination incrementing 10 more components, it works, only in the next pagination (which would result in a total of 35 components) the same error occurs.

The conclusion I had is that Flatlist has this limitation of rendering. But I can’t go through it, to have the effect of "Infinite Scroll" as seen on Instagram, for example.

My question then, is on how to create this Infinite Scroll correctly.

Follow the link of some discussions on similar problems, where I tried some proposed solutions without result.

https://github.com/facebook/react-native/issues/20011

https://github.com/facebook/react-native/issues/13649

  • https://github.com/Flipkart/recyclerlistview uses this lib, it is extremely perfomatic.

1 answer

1


Assuming that in your backend you have already done a pagination check and you have a call to return the total records.

With that in mind you’d have to control some states:

  1. Total number of Records.
  2. The Records.
  3. The pagination.
  4. Call to load more items.

Let’s name them and define their initial states here:

 const [registers, setRegisters] = useState([]);
  const [total, setTotal] = useState(0);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);

detail the pagination that must be started with the number 1 because there is no page 0

then we start making a call to load the data when starting the screen

useEffect(() => {
    loadRegisters();
  }, []);

the function called above will be responsible for loading new items in the Flatlist thus changing the states at the moment the component is rendered and when it is necessary to load more items.

async function loadRegisters() {
    if (loading) {
      return;
    }

    if (total > 0 && registers.length === total) {
      return;
    }

    setLoading(true);

    const response = await api.get("registers", {
      params: { page },
    });

    setRegisters([...registers, ...response.data.registers]);
    setTotal(response.headers["x-total-count"]);
    setPage(page + 1);
    setLoading(false);
  }

explaining the function loadRegisters: First upon starting the component, an api request will be made from your backend bringing the records with their due pagination and the total existing records. Making the application only search one page at a time.

  const response = await api.get("registers", {
      params: { page },
    });

then we define their state:

 setRegisters(response.data.registers);
 setTotal(response.headers["x-total-count"]);

This is enough to start your component, but we need to change the information whenever I need to. So whenever the user asks to bring more items:

  1. We make a restriction so that if the user asks to bring in more items repetitively it does not trigger shutdown while it is active.

      if (loading) {
          return;
        }
  2. After this is done a second screening that defines if all records have been loaded that it does not search for more records, because it would not make sense to do.

 if (total > 0 && registers.length === total) {
          return;
        }
  1. If the function goes through the two if’s we set the search state to TRUE thus preventing new unnecessary calls.

     setLoading(true);
  2. Then it is done again the call api to bring new information and we set them. attention to some details

    const response = await api.get("registers", { params: { page }, });

is passed in the request parameters the page number we want.

 setRegisters([...registers, ...response.data.registers]);

We set the records as an array, passing the records already set and the new ones that are coming, the way that was squeezed this action is the way to set 2 vectors within 1 vector in React or React Native. (the 3 dots describe the copy function)

setPage(page + 1);

This makes us move to the next page

setLoading(false)

and now we have released for the user to search the data again

Now on the Flatlist will need to add some properties first we have to onEndReached which is a function that is activated automatically whenever the user arrives at the end of the page:

<FlatList 
      ListHeaderComponent={/*irrelevante*/}
      data={this.props.locals} 
      keyExtractor={item => item.id} 
      renderItem={this.renderLocal} 
      onEndReached={loadIncidents}
/> 

bonus: If you want to render new records before the user reaches the end we have the property onEndReachedThreshold that a floating number is passed telling how many % before reaching the end should be brought new records, example: 0.1 = 10%, 0.2 = 20% before the end and so on. would look something like this

<FlatList 
      ListHeaderComponent={/*irrelevante*/}
      data={this.props.locals} 
      keyExtractor={item => item.id} 
      renderItem={this.renderLocal} 
      onEndReached={loadIncidents}
      onEndReachedThreshold={0.2}
/> 

Browser other questions tagged

You are not signed in. Login or sign up in order to post.