What is Infinite Scroll?
Infinite Scroll is a web design technique that progressively loads items in a scroll-able list as a user scrolls. The technique can be viewed as a progressive loading approach to lists. It should be noted that the User Experience is quite different compared to alternative solutions such as pagination. That is to say, Infinite Scroll might be great for your app, but it might not be great for every app!
Nevertheless, this How-To will show you how easy it is to implement Infinite Scroll using SlashDB and React. The same SlashDB API can be used for implementing Pagination.
Demo: Infinite Scroll with SlashDB and React
Overview
We are going to build a simple React application and will use an existing Infinite Scroll React Component so that we can focus on the integration with SlashDB. First, we will set up our app. Then we will implement the Infinite Scroll with simple numbered divs. Finally, we’ll integrate the SlashDB API.
You can reference the working source code here. Let’s get started!
React App Setup
Create React App
First things first, we will create our React app with the create-react-app npm package.
Run:
npx create-react-app infinite-scroll
or
npm init react-app infinite-scroll
Start the App
Next, we will start the app to make sure it is functional.
Change directories:
cd infinite-scroll
then run:
npm start
The console should indicate a successful deployment. Then you can navigate to localhost:3000 using a web browser.
If you see a React Landing Page, setup is complete!
Test Infinite Scroll
Pull Down the InfiniteScroll Component
Next, we will download the Infinite Scroll Component.
We will be using an npm package called react-infinite-scroll-component. Pull down the package from npm by running:
npm install ---save react-infinite-scroll-component
Implement a Test
Now we can implement a test of the Infinite Scroll Component.
To achieve this we will make several updates to the src/app.js file. These updates include:
- Import the react-infinite-scroll-component.
- Update the App function to return the InfiniteScroll Component.
- Initialize some state variables
- Implement a loadMore function with a timeout to replicate the behavior of an external API.
Import InfiniteScroll
Add the following import statement to the top of App.js:
import InfiniteScroll from 'react-infinite-scroll-component';
Render InfiniteScroll
Next, update the return statement of App() to return the following:
<div className="App" style={{ width: '300px', margin: 'auto' }}> <h1>Infinite Scroll!</h1> <InfiniteScroll height={250} dataLength={list.length} next={loadMore} hasMore={hasMore} loader={<h4>Loading...</h4>} endMessage={ <p> <b>End of List</b> </p> }> {list.map((_i, index) => ( <div key={index}> #{index} </div> ))} </InfiniteScroll> </div>
Let’s walk through this briefly. We replaced the contents of the “App” div with a header and the InfiniteScroll Component. The InfiniteScroll Component has several attributes:
- height – designates that the infinite scroll has a height and implies that the scroll is associated to a child of the window, rather than the window itself.
- dataLength – the current length of the list that we have loaded and which is being rendered as children of the InfiniteScroll Component.
- We reference a variable called ‘list.’ This is going to be part of our state which we will get to shortly.
- next – this is a function that InfiniteScroll uses to load more list items. We will discuss that soon, too.
- hasMore – this is a boolean variable that indicates to InfiniteScroll that we can load more. If we can’t, it displays an indicator to the user.
- This will also be part of our state
- loader – this is what InfiniteScroll should display while it waits on next to load more items.
- endMessage – this is what InfiniteScroll should display when there are no more items to load.
Inside InfiniteScroll we render our list as an array of numbered divs for this test.
You’ll also notice a style applied to the “App” div. This is simply to narrow the scroll-able div and to center it in the window.
Add State Variables
Since App is a Functional Component, rather than a Class Component, we will use the React useState Hook to add the state variables mentioned above. At the beginning of the App function add the following:
let [list, setList] = useState(Array.from({ length: 200 })); let [hasMore, setHasMore] = useState(true);
If you aren’t familiar with React Hooks, you can read more about them here.
Let’s discuss the motivation for these values…
list is what InfiniteScroll will display and so should have an initial value. We gave it an array of length 200 for this test. We will also need to update list, which we will do next in the loadMore function.
hasMore is our boolean indicator that signals that we can load more items. Initially, the value should be true, but we will need to update it in the loadMore function when there are no more items to load.
Implement loadMore
Here, we will tie everything together.
Here is the implementation for the test:
let loadMore = () => { if(list.length <= 500) { setHasMore(false); } else { setTimeout( () => { setList(list.concat(Array.from({ length: 20 }))); }, 500); } }
We are simulating an external API here. First we check if list contains more than 500 items. This is intended to simulate reaching the end of the data that an API would return. If we haven’t reached the limit, we concatenate twenty more items to the existing list. We do so using a timeout of 500 milliseconds to simulate the API call.
At this point, the test is functional! We can demonstrate the progressive loading of items with the “loading…” display, as well as the “End of List.” Now that we have all of this in place, integrating SlashDB will be a breeze!
What we’ve finished up to this point is committed to the GitHub repository with the message complete test implementation.
Take a look at what we have so far:
Integrate SlashDB
All we need to accomplish now is:
- Initialize list with data from SlashDB
- Update list with data from SlashDB
- Update how we render the items
Before digging into the code again. Let’s show how simple it will be to retrieve progressive sets of data using the SlashDB API. By now, you are probably familiar with the general format of SlashDB URLs. In this demo, we will use https://demo.slashdb.com/db/Chinook/Invoice.json to retrieve a JSON list of Invoices from the Chinook database hosted on demo.slashdb.com. The URL linked above retrieves the entire list, so we need to alter it to achieve our goal.
Enter: Query Parameters.
SlashDB allows us to add parameters to the URL to further tailor our queries! Read more about them here. The parameters we will use in this example are limit and offset.
limit lets us tell SlashDB not to return more items than we specify, whereas offset allows us to indicate the starting index that SlashDB should include in the data. Together, these two parameters allow for our infinite scroll functionality. For example, https://demo.slashdb.com/db/Chinook/Invoice.json?limit=5&offset=1 returns 5 invoices and skips the first (the index starts at 0).
To use these parameters, we will need a new state variable, offset, that we can reference and update as the scrolling occurs. Depending on your app, you might want to allow limit to be different on initialization and update. That’s how we will demonstrate it here.
With that understanding, we are in a good spot to get our demo working!
Update State Initialization
Here we will update our existing state hooks, add the new offset state variable, and initialize list with data from SlashDB by using React’s useEffect Hook.
Here’s what we end up with:
// initialize state let [list, setList] = useState([]); let [hasMore, setHasMore] = useState(true); let [offset, setOffset] = useState(0); // load data from API into list on Mount useEffect(() => { load(50); }, []);
We set list to an empty array to start and hasMore is unchanged. We add offset with an initial value of 0 so that we start loading data from the first index.
useEffect with an empty array as the second parameter allows us to run the function we provide on Mount only. This is perfect for initializing state using data retrieved from an API. We provide a function called ‘load’ that will load data from SlashDB and update the state accordingly. The parameter is a limit that we will add to the URL. We will discuss the implementation of load shortly.
Update State-Updates
Recall that InfiniteScroll required the definition of a function to update the items. It was referred to as next. Before we go write a function for this, consider that the load function does just what we want! Let’s use load in the next function then implement load. This way load pulls double duty; we can use it to initialize and update list.
Update the InfiniteScroll Component to use load.
next={ () => { load(20}; }
Now for load:
let load = async (limit) => { let res = await fetch(slashDbUrl(limit, offset)); let json = await res.json(); if(json.length > 0) { // if we have results, add them to the list and update the offset setList(list.concat(json)); setOffset(offset + limit); } else { //else we have nothing else to load setHasMore(false); } }
As you can see, load is an asynchronous function that loads data using a URL returned from a function called slashDbUrl which we pass a limit and the offset from the state. Then the state is updated according to the response. In the case that the length of the payload is not 0, the data is concatenated to the existing list and the offset is updated. On the other hand, in the case in which the response is an empty array, hasMore is set to false, indicating that we have retrieved all of the available data.
Let’s take a look at slashDbUrl:
let slashDbUrl = (limit, offset) => { let base = "https://demo.slashdb.com/db/Chinook/Invoice.json"; return `${base}?limit=${limit}&offset=${offset}` }
Just as we described at the beginning of this section, each URL we use will be a variation of the base with limit and offset parameters that vary. We pass state to this function so that the offset aligns with the next index at which to start collecting data.
Update Item Rendering
Now that we are loading invoices, we should display data from them! We’ll simply update the array of children that we pass to InfiniteScroll, like so:
{list.map((invoice, index) => ( <div key={index} style={{display: 'flex', justifyContent:'space-evenly'}}> <div>{invoice.InvoiceDate.split('T')[0]}</div> <div>${invoice.Total}</div> </div> ))}
Here we display the invoice date and total for each invoice in a row.
Finally, we are done. Check out the source on Github at the commit implement slashdb infinite scroll.
Here’s the final result:
Conclusion
There we have it: Infinite Scroll using SlashDB and React! As we’ve demonstrated, the integration to progressively load data from SlashDb is as simple as maintaining a state variable and adding it as a parameter to our URL! As noted, this same API can be used to implement pagination if you find it better suits your UI/UX requirements. With SlashDB you can take your application to infinity, and beyond!