Building An Infinite Scroll Into Your React App
Tags: react, reactlearning, reactiveprogramming
Published: 2022-06-08 15:57:07

### Introduction
Infinite scroll has become a major feature in apps we use in our day to day life like Twitter Instagram and just generally content feed apps that just want your undivided attention daily, on a functionality point of view infinite scroll outperforms pagination approaches to loading data due to it being seamless to the user and only loads more data when the user reaches the end of the scroll.

#### Infinite scroll
`Infinite scroll` is  a feature where data is loaded onto the user’s page when the user reaches the end or almost the end of the scroll page, this is done by calling a paginated API,
A paginated API for reference is an API that returns a list of data whenever we call the API and can return different sets of data based on the page count that we passed into it an example of a paginated API would be the API we use in this example

```
`https://jsonplaceholder.typicode.com/photos?_page=${page}&_limit=10`
```

`page` is the variable we pass into the API it is going to be a number that we track and increment after loading every page. 

`Infinite scroll` although an excellent approach to loading data isn't the most optimal  for all projects,  some projects do function better with pagination, but infinite scrolling works best when loading related data that is loaded in a preferably chronological order based on time or relevance, pagination though is useful when users need to load data that far back, let's say you have some bank transaction records and you know that the records are a month away you can skip to the farthest page and circle back if you overshoot the page,  but in reality, infinite scroll and a good date filter  can solve that problem 

#### Prerequisite
Building this application would require some basic knowledge of a couple of things we would be using in our application.

* React
* Javascript
* REST API’s 

### Implementation
In react we have 2 options to implement infinite scroll in our app.

* Using an exciting library (The smart boring way)
* Implement the infinite scroll (The fun slow way)

#### Using an exciting library (The smart boring way)
A fast way to implement infinite scrolling in react would be to use a third-party library one of my go-to libraries for this feature would be the `react-infinite-scroll-component`.

`react-infinite-scroll-component` is a simple library that exports an `<InfiniteScroll/>` component that can be used in our application and its feature-rich with props and events you can call before and after loading more data into the app, also a cool thing would be a refresh function you can call whenever you want to load new data to the top of your table.

####Installing
```
 npm install --save react-infinite-scroll-component
```

  or

 ```
yarn add react-infinite-scroll-component
```


In our `App.jsx`

```
 import React from "react";
import InfiniteScroll from "react-infinite-scroll-component";
import axios from "axios";
 
let page = 1;
const fetchData = (setItems, items) => {
 axios
   .get(`https://jsonplaceholder.typicode.com/photos?_page=${page}&_limit=10`)
   .then((res) => {
     setItems([...items, ...res.data]);
     page = page + 1;
   });
};
 
const refresh = (setItems) => {};
 
export default function App() {
 const [items, setItems] = React.useState([]);
 
 React.useEffect(()=>{
   fetchData(setItems,items)
 },[])
 return (
   <InfiniteScroll
     dataLength={items.length} //This is important field to render the next data
     next={() => {
       fetchData(setItems, items);
     }}
     hasMore={true}
     loader={<h4>Loading...</h4>}
     endMessage={
       <p style={{ textAlign: "center" }}>
         <b>Yay! You have seen it all</b>
       </p>
     }
     // below props only if you need pull down functionality
     refreshFunction={refresh}
     pullDownToRefresh
     pullDownToRefreshThreshold={50}
     pullDownToRefreshContent={
       <h3 style={{ textAlign: "center" }}>&#8595; Pull down to refresh</h3>
     }
     releaseToRefreshContent={
       <h3 style={{ textAlign: "center" }}>&#8593; Release to refresh</h3>
     }
   >
     <div style={{ minHeight: "100vh" }}>
       {items.map((user) => (
         <img src={user.url} height="100px" width="200px" />
       ))}
     </div>
   </InfiniteScroll>
 );
}
 ```

Let's break our code down into smaller bits.


```
let page = 1;
const fetchData = (setItems, items) => {
 axios
   .get(`https://jsonplaceholder.typicode.com/photos?_page=${page}&_limit=10`)
   .then((res) => {
     setItems([...items, ...res.data]);
     page = page + 1;
   });
};
 ```
 
The `fetch` function is able to call our API to get new data it's triggered by the `<InfiniteScroll/>` component when we scroll to the end of the view, there is a count variable we use to monitor the page loaded and it is incremented after the data is loaded.


```
 const [items, setItems] = React.useState([]);
 React.useEffect(()=>{
   fetchData(setItems,items)
 },[])
```

React effect is used to load the first batch of data into the view, we pass the systems function and the items variable into the function (something new I should have been doing a while back to remove API calls from my component)

```
<InfiniteScroll
     dataLength={items.length} //This is important field to render the next data
     next={() => {
       fetchData(setItems, items);
     }}
     hasMore={true}>
/////// 

/// code
///////
>
     <div style={{ minHeight: "100vh" }}>
       {items.map((user) => (
         <img src={user.url} height="100px" width="200px" />
       ))}
     </div>
   </InfiniteScroll>
 ```

We call our component and pass data into it if you need documentation you can check it out here `https://www.npmjs.com/package/react-infinite-scroll-component`.

Here is the output.

![Screen Shot 2022-05-22 at 4.05.48 PM.png](https://functionalworks\-backend\-\-prod.s3.amazonaws.com/logos/63994c51f91d4fd976945fbf5556c808)


#### Implement the infinite scroll (The fun way)
Implementing a scroll component can be a nice learning project and gives you more control than when you use a component and is pretty easy to set up but can take a bit of time to perform research on how to get it done, luckily I've done that for you.

#### Pros of using custom components
* Customizable
* Very light since its just one component 

#### Cons
* Takes a bit of time to set up
* It May is not as robust as an already built component

#### Here is our codebase
```
import React, { Component } from "react";
 
class ScrollComponent extends Component {
 constructor() {
   super();
   this.state = {
     loading: false,
     page: 0,
     prevY: 0
   };
 }
 
 async getItems() {
   try {
     await this.props.loadData();
   } catch (error) {
     console.log(error);
   }
 }
 
 componentDidMount() {
   this.getItems();
 
   var options = {
     root: null,
     rootMargin: "0px",
     threshold: 1.0
   };
 
   this.observer = new IntersectionObserver(
     this.handleObserver.bind(this),
     options
   );
   this.observer.observe(this.loadingRef);
 }
 
 async handleObserver(entities, observer) {
   const y = entities[0].boundingClientRect.y;
   if (this.state.prevY > y) {
     this.setState({ loading: true });
     console.log(this.state);
 
     await this.getItems();
 
     this.setState({ loading: false });
     console.log(this.state);
   }
   this.setState({ prevY: y });
 }
 
 render() {
   // Additional css
   const loadingCSS = {
     height: "100px",
     margin: "30px"
   };
 
   // To change the loading icon behavior
   const loadingTextCSS = { display: this.state.loading ? "block" : "none" };
 
   return (
     <div className="container">
       <div style={{ minHeight: "800px" }}>
         {/* {this.state.photos.map(user => (
          <img src={user.url} height="100px" width="200px" />
        ))} */}
         {this.props.children}
       </div>
       <div
         className="house"
         ref={(loadingRef) => (this.loadingRef = loadingRef)}
         style={loadingCSS}
       >
         <span style={loadingTextCSS}>Loading...</span>
       </div>
     </div>
   );
 }
}
 
export default ScrollComponent;
``` 
 
 

And in our `app.jsx` component we replace the `<InfiniteScroll/>` and insert our new component.

```
import React from "react";
import axios from "axios";
import ScrollComponent from "./scroll";
 
let page = 1;
const fetchData = async (setItems, items) => {
 const data = await axios.get(
   `https://jsonplaceholder.typicode.com/photos?_page=${page}&_limit=10`
 );
 
 setItems([...items, ...data.data]);
 page = page + 1;
};
 
const refresh = (setItems) => {};
 
export default function App() {
 const [items, setItems] = React.useState([]);
 
 React.useEffect(() => {
   fetchData(setItems, items);
 }, []);
 return (
   <ScrollComponent
     loadData={() => {
       fetchData(setItems, items);
     }}
   >
     <div style={{ minHeight: "100vh" }}>
       {items.map((user) => (
         <img
           key={Math.random()}
           src={user.url}
           height="100px"
           width="200px"
         />
       ))}
     </div>
   </ScrollComponent>
 );
}
 ```


Let's break our component down into smaller bits so we can understand it.


##### Part 1
```
 componentDidMount() {
   this.getItems();
 
   var options = {
     root: null,
     rootMargin: "0px",
     threshold: 1.0
   };
 
   this.observer = new IntersectionObserver(
     this.handleObserver.bind(this),
     options
   );
   this.observer.observe(this.loadingRef);
 }
```

Our `componentDidMount` function is run as soon as our component is started and adds an   `IntersectionObserver`  observer to the component that checks out the `house` and measures the difference between it and the  `this.props.children` and calls the `handleObserver`  function when the observer is triggered.

```
 async handleObserver(entities, observer) {
   const y = entities[0].boundingClientRect.y;
   if (this.state.prevY > y) {
     this.setState({ loading: true });
     console.log(this.state);
 
     await this.getItems();
 
     this.setState({ loading: false });
     console.log(this.state);
   }
   this.setState({ prevY: y });
 }
 ```

Our `handleObserver` example function calls out the update function that's passed into the props, this is powerful because we can use the concept of dependency injection to pass in the update function from our component making this component agnostic to its use case 

```
 const [items, setItems] = React.useState([]);
 
 React.useEffect(() => {
   fetchData(setItems, items);
 }, []);
```

We take advantage of react useEffect to set up how we manage data in our component, we need to pass `setItems` and `items` into  the fetchdata component to pass control initoo the function, 

 ```
 render() {
   // Additional css
   const loadingCSS = {
     height: "100px",
     margin: "30px"
   };
 
   // To change the loading icon behavior
   const loadingTextCSS = { display: this.state.loading ? "block" : "none" };
 
   return (
     <div className="container">
       <div style={{ minHeight: "800px" }}>
         {/* {this.state.photos.map(user => (
           <img src={user.url} height="100px" width="200px" />
         ))} */}
         {this.props.children}
       </div>
       <div
Class = ‘house’
         ref={(loadingRef) => (this.loadingRef = loadingRef)}
         style={loadingCSS}
       >
         <span style={loadingTextCSS}>Loading...</span>
       </div>
     </div>
   );
 }
```

Our render function renders our child component passed into the component, this lets us reuse our component for different types of use cases.

Replacing our component in the `App.js`
```
 <ScrollComponent loadData={()=>{
     fetchData(setItems, items);
   }}>
     <div style={{ minHeight: "100vh" }}>
       {items.map((user) => (
         <img src={user.url} height="100px" width="200px" />
       ))}
     </div>
   </ScrollComponent>
```

Our output(similar to our old implementation).


![Screen Shot 2022-05-22 at 4.05.48 PM.png](https://functionalworks\-backend\-\-prod.s3.amazonaws.com/logos/63994c51f91d4fd976945fbf5556c808)


### Conclusion
`Infinite scrolling` is becoming an amazing way of showing feed data because it offers a nonstop flow of data that is addictive (talking from a users point of view) and only loads new data when it reaches the end of the page, this is done by monitoring the page count and incrementing the page seen page at the end of every load.

In this guide, we learned 2 different modes of implementing this feature in react, 
 
* Using an exciting library (The smart boring way)
* Implement the infinite scroll (The fun way)

Each approach gives the same result but comes with different pros and cons that make them perfect for different situations, I personally keep a copy of my own custom component on my PC and copy the custom component into my new project,  it helps to keep it flexible for different project seeing as it is just a component and can be called whenever it's needed, also the concept of injecting the load function makes it easy to use and re-use across projects.

I hope this article was helpful to you, cheers and till next time!

#### Referrence 
* [Gaurav Singhal](https://www.pluralsight.com/guides/how-to-implement-infinite-scrolling-with-reactjs)
 



Originally written by [King Somto](https://javascript.works-hub.com/users/8cedbde0-2e7b-4328-8741-f8c981f0b076?utm_source=dev_to&utm_medium=blog_xpost) for [JavaScript Works](https://javascript.works-hub.com/learn/building-a-modular-infinite-scroll-252dd?utm_source=dev_to&utm_medium=blog_xpost)