Introduction
As a Frontend Engineer, the beauty of an application does not just rely on the looks, it also has a lot to do with flexible access to an application. Nowadays, uncountable data is fetched from the server/API and if not handled properly you may end up crashing an application.
Also, you may have been in a situation where you want to allow users to fetch their data seamlessly without having to let them click on a button before getting more batching of their data.
If you had wished to perform the above-mentioned and have a seamless flow of data while scrolling on the window, perform pagination from the frontend, use React-Query — use query hook to manage requests, and learn best practices, then this article is for you.
Things to Learn
In this article, I will be guiding you through how to do the following:
- Manage users' data using ReactJs hooks,
- How to use react-query, useQuery hook to handle requests,
- Handle infinite scroll,
- Handle pagination from the frontend,
Prerequisites:
- Know Javascript
- Know ReactJS Basic
- Install Node/Npm
Setting Up Your Environment
To create a new react project, use the following link, and name it react-infinite-loop. In your terminal, let's install axios and react-query as shown below:
cd Desktop
cd react-infinite-loop
npm i react-query -save
npm i -D axios -save
code .In the snippet above, I went into the desktop folder, navigated to the project I created, and installed two dependencies, react-query which is a hook that helps me manage HTTP requests, a library, axios for making a request and finally opened the project in vscode with the code dot(.).
Initializing React Query
Let's go into the app component and add the following as shown below:
In the snippet above, an instance of the client was created, assigned to the client variable, and later passed into the client parameter of the QueryClientProvider to enable all the wrapped components to have access to the client.
Create Folder Structure
Let's go to the terminal and run the following:
cd src
mkdir Component Query View
cd Component
touch List.jsx
cd ..
cd View
touch Home.jsIn the snippet above, I went into the src folder and created three different folders named Component, View, and Query. I created two files, List.jsx which is inside the component folder, and the Home.js file which is inside the View folder.
Request Handler
Inside the Query folder, I created above, create a file and name it util.js, inside it add the following code snippet:
In the snippet above, I created a fetchPost function that will handle the fetching of posts from an API, a search query of page and limit was passed along with the request in other to limit the amount of data that will be fetched at once from the API and I also set the Content-Type of the request to application/json and set the encoding format to UTF-8.
The page and limit are parameters that will be passed from where I will be executing the fetchPost function. So because I will be using the useQuery hook as my API manager, I destructured the queryKey property. The queryKey property will be an array that has two properties, a key for the request and other options(page/limit).
Because I only needed the options, I had to use the comma to split out the key to the request and then destructured the page and limit. You can read more on react-query(useQuery) here.
Fetch Request Using UseQuery Hook
Let's go into the Home component I created above and add the following to it:
Previously, I imported the Home component into the app component that has not been created, inside it, I created a default state that will handle the page, limit, and the posts that will be fetched. For now, I am returning a text.
Let's move on to fetch the posts onMount of the page using the useQuery hook from react-query as shown below:
In the snippet above, I used the useQuery hook to manage the post request once the Home component is mounted, I imported the fetchPost function that handles API calls as a parameter to the useQuery and passed an array that holds the request queryKey and other options (page and limit).
I used the retry property to rerun the request if failed initially, I set the delay to three seconds before retrying the request and avoided making a request to the API on every focus on the window. UseQuery hook gives us an onError and onSuccess properties that manage the error and success response of the request. So once the request is successful, I set the returned data to the post-state I created above.
Render Fetched Data
In the snippet above, When the Home component is mounted, I used a conditional rendering to check if posts were returned from the HTTP request I made by checking if the length of the post-state is greater than zero.
Once it is greater than zero, I loop through the post array and destructure the post properties that will be displayed to the users and pass them as a prop to the List Component I created earlier.
Remember, I destructured the isFetching and isLoading from the useQuery Hook, those are used as a flag to monitor when a request is trying to fetch some data and when it's updating the cached data with the recent change in the data.
Let's add the below code snippet to manage what happens while I wait for my request to be completed:
Just after the rendering of posts, add the snippet above to show the user that the post is yet to be fetched.
Let's go into the List component and render the props as expected as shown below:
I destructured the props and rendered them in their respective elements. Let's go into the browser and take a look at what I have done so far in the snippet below:
In the snippet above, I have successfully rendered the fetched posts to the browser, the title, userId, and the body of the posts. Now that the post is rendered, let's look at how to load more posts once I reach the end of every page fetched.
Infinite Scroll (Pagination on Scroll)
For us to be able to fetch the next page of the data on scroll, there are checks I need to set in place, these checks are as follows:
- Get when the user scrolls to the bottom,
- Set a flag,
- Add the next page data to the previous fetched
Get when the user scrolls to the bottom
Inside the Home component, let's add the following code snippet:
In the snippet above, I used a hook called useLayoutEffect, this hook is imported from the react library, it is useful when you want to read the layout from the DOM and synchronously re-render.
ScrollTop; is used to get the height of the document on scroll starting from zero, scrollHeight; is used to get the browser height, and clientHeight: is used to get the visible height of the document, all were gotten from the document body, and lastly I passed the handleScroll function to the event listener to execute anytime the user scrolls.
Inside the handleScroll method, just after the declarations add the following code snippet:
Set a flag
Above, I minus the total current height of the document(scrollTop) from the total height of the document(scrollHeight) and check if it's equal to the visible height of the document(clientHeight) on the d scroll and assigned the value to the setPaginateFlag — a boolean value.
Afterwards, I checked if the user has reached the bottom of the visible height, if it's true, I set a flag to a state called setMorePosts, if morePosts is true and there is more data to fetch, then I fetch the next data.
Previously the useLayoutEffect is set to execute onMount, but I also want it to rerun when the morePosts and nextPage are updated, so let's set the morePosts and nextPage in the useLayoutEffect dependency array as shown below:
The setMorePosts and the setNextPage state are declared above the useQuery function as shown below:
Handle Pagination
Most frontend engineers prefer pagination to be handled by the backend engineers. I am using fake data from JsonPlaceholder so let's handle the total page and the next page of the data in the following code snippet:
By default, the total data from the API is one hundred (100). I divide the total number of data by the amount of data per page to get the totalPage. I checked if the totalPage is greater than the current page.
If yes, I increase the page by 1 which gets the next page, else, I return the current page and finally set the nextPage to the nextPage variable — this happens each time the request is successful. The limit variable becomes one if I set it to hold a value that is less than one.
It should be noted that for uncountable data you may have to fetch all and then write the above algorithm for pagination or rather have the total count sent to you by the backend engineer.
Add the next page data to the previous fetched
Next, I will have to merge the next page data to the current data as shown in the following snippet:
In the snippet above, I adjusted the setPosts function and used a spread operator to concatenate the current posts and the next batch of posts fetched from the request.
The demo
I guess is time to take a look at what I have done so far using the following demo link.
Conclusion
Finally, from the demo above, it's obvious that the infinite Scroll is working seamlessly. I believe from now you should stop using third-party observers, use useQuery to fetch data from an API, handle pagination from the frontend, handle batching of data and finally allow users to fetch more of their data without clicking a button preferably on scroll on the window.
If this article was helpful to you, Kindly like, comment and follow me on Linkedin and Medium to see more of my article. Click on the following link to access the source code.
Feel free to drop any suggestions/comments. if you have a topic based on the Javascript/React.js ecosystem, kindly reach out and I'd be glad to write on it. Thanks.
