React Native Custom Infinite Scroll

Anur Be─çirovi─ç

When I started working with React Native recently, I did a lot of research on infinite scroll. There are many proposed solutions online, even libraries ready to be used, but none of them were quite right for me. That is why I created one for myself, and this blog will show you how you can do it too.

Step 1: The idea
I wanted to create a picture app that fetches data from API and shows a list of pictures with a title and description. Previous experience taught me that getting the whole list and rendering it on screen can be really bad for performances of your application (Web or Mobile) so I decided to limit the response to 5 items per page :

"pictures": {
        "current_page": 1,
        "data": [...],
        "last_page": 8,
        "per_page": 5,
        "total": 40
}

As you can see from the response, there are 40 items in total, with 5 items per page on a total of 8 pages.

Step 2: Setting the scene­ƒô£
First we need to fetch the data from API. For this I will use axios.
(You can find the full documentation here)

npm install --save axios

After installing axios, we need to create a new instance of axios with our API URL, in root folder of our application (If you are not sure where the root folder is, just make it appear in the same place as App.js file) and name it “axios.js”

import axios from 'axios';
var instance = axios.create({
      baseURL: 'https://YOUR_URL/api'
});
export default instance;

After creating the instance, we can start implementing our infinite scroll.

First, create a new component called “ImageList.js” and add all imports that we will use for creating and rendering pictures:

import React, { Component } from 'react';

import {Text,View,ScrollView,TouchableOpacity} 
from 'react-native';

import API from './axios';

As you can see, we added a few things that we will use for this component. The most important is ScrollView and we will use it for onScroll event, a few steps later.

Step 3: The Coding
We can now start creating our class component. To do that, we need to define our state and create a life cycle method componentWillMount().

export default class ImageList extends Component {

 state = {
    loading: false,
    data: [],
    current_page: 1,
    error: null,
    hasMore: true
  }

  componentWillMount() { this.getListOfPictures(); };

}

The function getListOfImages() is then called, like you can see in the next block of code. A similar function was found on stack overflow but it didn t work for me, so I modified it. This is what I came up with:

getListOfPictures = () => {
 if (this.state.loading) { return; }
 this.setState({ loading: true });
   API.get(`/pictures?page=${this.state.current_page}`)
      .then(res => {
      const nextPictures= res.data.pictures.data.map(data => ({
        title: data.title,
        image_url: data.image_url,
        description: data.description,
        id: data.id
   }));
      this.setState({
        hasMore: ( res.data.pictures.current_page <=         
                   res.data.pictures.last_page),        
        data: [...this.state.data, ...nextPictures],
        loading: false,
        current_page: this.state.current_page + 1})
     }).catch(error => {this.setState({ error, loading: false });});
}

The getListOfImages() function allows us to get images from our API. As you can see in the window above, we need to set our loading to true. We then use our instance of axios and make a request to the API with the starting page. (Remember, we have 8 pages with 5 items on each page – 40 items in total).

Next, we add our data to nextPicture variable where we hold the 5 items, and append it to our data array. For hasMore property we will return true or false based on current_page and last_page, and lastly for current_page we will just increment value after each request. For rendering list of pictures we will use ScrollView:

render() {
  return (
    <ScrollView onScroll={({ nativeEvent }) => {
      if (this.isCloseToBottom(nativeEvent) && this.state.hasMore) {                
           this.getListOfPictures(); }}}> 
      {this.renderList()} 
    </ScrollView>
}

As you can see, we have a few steps to follow to make this work. The first function that we need is “isCloseToBottom”, and the second is “renderList”.

isCloseToBottom({ layoutMeasurement, contentOffset, contentSize }) {   return layoutMeasurement.height + contentOffset.y 
       >= contentSize.height - 50; }

This function will return Boolean value based on height and content of the screen.

Lastly, we will implement our function to actually see our images with title and description. For displaying pictures I used  react-native-elements Card component, but you can use this code for any type of custom View. If you want to follow my example you can find the full documentation for react-native-elements here.

renderList = () => {
  return ( this.state.data.map((u) => {
    return ( 
      <TouchableOpacity
          key={u.id} }}>
           <Card 
              featuredTitle={u.title}
              image={{ uri: u.image_url }}
              imageStyle={styles.image}>
              <View style={{ padding: 10 }}>
                 <Text style={{ fontSize: 15}}>Description:</Text>        
                 <Text>{u.description}</Text>
              </View>
            </Card>
       </TouchableOpacity>);})
 );
}

I hope this article will help you implement infinite scroll anywhere you need.

If you experience any roadblocks, have uncertainties, or maybe suggestions on how to improve this code, let me know in the comment section below.

Leave a Reply

Your email address will not be published. Required fields are marked *

After you leave a comment, it will be held for moderation, and published afterwards.


The reCAPTCHA verification period has expired. Please reload the page.