Load more in React, Node.js & MongoDB
Previously, we wrote 2 articles on how to add a “load more” button. First article uses Node.js and MongoDB and EJS for frontend and second article uses PHP and MySQL. In this article, we will show you, how you can create a “load more” button in React with Node.js and MongoDB as your backend. This is the refined version of previous load more tutorial in Node.js and MongoDB.
First, let me show you all the code. Then I will explain everything.
React
function App() {
// Set current page
const [page, setPage] = React.useState(1);
// Data array
const [users, setUsers] = React.useState([]);
// Check if there are more pages
const [hasMorePages, setHasMorePages] = React.useState(false);
async function loadMore() {
// Creating a built-in AJAX object
var ajax = new XMLHttpRequest();
// Tell the method, URL of request and make is asynchronous
ajax.open("POST", "http://localhost:3000/fetch-users", true);
// Detecting request state change
ajax.onreadystatechange = function () {
// Called when the response is successfully received
if (this.readyState == 4) {
if (this.status == 200) {
// For debugging purpose only
// console.log(this.responseText);
// Converting JSON string to Javasript array
var response = JSON.parse(this.responseText);
// Get data from API
const newArr = response.users;
// Get current data
const tempArr = [ ...users ];
// Loop through all new records
for (let a = 0; a < newArr.length; a++) {
// And add them in existing array
tempArr.push(newArr[a]);
}
// Update state variable
setUsers(tempArr);
// Set if there are more pages
setHasMorePages(response.hasMorePages);
}
}
};
// Create form data object
const formData = new FormData();
// Attach current page number
formData.append("page", page);
// Send the request
ajax.send(formData);
}
// Run function when page value updates
React.useEffect(function () {
loadMore();
}, [page]);
return (
<>
{/* Display data */}
{ users.map(function (user) {
return (
<p key={ `user-${ user._id }` }>{ user.name }</p>
);
}) }
{/* Show load more button if there are more pages */}
{ hasMorePages && (
<button type="button"
onClick={ function () {
// Increment page value
// It will automatically call the loadMore function from useEffect hook
setPage(page + 1);
} }>Load more</button>
) }
</>
);
}
Here, we are displaying all the data we are getting from API. We are displaying the “load more” button only if there are more pages required. We created a React.useEffect hook and calling the loadMore function everytime the page value gets updated. It will automatically gets called first time when the page loads. After that, every time you click the “Load more” button, we are simply incrementing the page value by 1. It will trigger the React.useEffect hook, thus calling the loadMore function again.
From API, we will receive the data that will be an array, and a boolean variable that tells if there are more pages required. Based on the latter, we will show or hide the “Load more” button. If there are more pages required, you can keep pressing load more button. When there are no more records in the database, then the load more button will be removed.
When the response from API is received, we are appending the new data in our existing data array. The way to do that in React is different. First, we need to create a shallow copy of our existing array using spread operator: const tempArr = [ …users ];
Then we need to loop through all new data received from API, and push it in your shallow copy of array. Finally, we will update our state variable and will render the new data.
For rendering, we are using Javascript map function to iterate over an array. In each paragraph, we should have a unique key value for React to identify each element uniquely. In order to create a unique key, we are using user ID, and we are displaying user name in each paragraph. This is create a “load more” button in our React component. In next step, we will create it’s API.
Node.js & MongoDB
// Run command in your terminal:
// npm install express http mongodb cors express-formidable
const express = require("express");
const app = express();
const http = require("http").createServer(app);
const mongodb = require("mongodb");
const ObjectId = mongodb.ObjectId;
const MongoClient = mongodb.MongoClient;
const cors = require("cors");
app.use(cors("http://localhost/load-more.html"));
// Native way to allow CORS
/*// Add headers before the routes are defined
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader("Access-Control-Allow-Origin", "*");
// Request methods you wish to allow
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE");
// Request headers you wish to allow
res.setHeader("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Authorization");
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader("Access-Control-Allow-Credentials", true);
// Pass to next layer of middleware
next();
});*/
const expressFormidable = require("express-formidable");
app.use(expressFormidable({
multiples: true
}));
const port = process.env.PORT || 3000;
const databaseName = "test"
const MONGO_URI = process.env.MONGO_URI || "mongodb://127.0.0.1:27017";
http.listen(port, async function () {
console.log("Server started");
const client = new MongoClient(MONGO_URI);
try {
await client.connect();
const db = client.db(databaseName);
console.log("Database connected.");
// Seeding the data in database
/*const usersCount = await db.collection("users").countDocuments({});
if (usersCount == 0) {
await db.collection("users")
.insertMany([
{ name: "Adnan" },
{ name: "Afzal" },
{ name: "Ahmad" },
{ name: "Khalid" },
{ name: "Tariq" },
]);
}*/
app.post("/fetch-users", async function (request, result) {
const limit = 1;
const page = parseInt(request.fields.page || 1);
const skip = (page - 1) * limit;
const users = await db.collection("users")
.find({})
.sort({
_id: 1
})
.skip(skip)
.limit(limit)
.toArray();
const usersArr = [];
for (let a = 0; a < users.length; a++) {
const obj = {
_id: users[a]._id || "",
name: users[a].name || ""
};
usersArr.push(obj);
}
const totalCount = await db.collection("users").countDocuments({});
const hasMorePages = (page * limit) < totalCount;
result.json({
status: "success",
message: "Data has been fetched.",
users: users,
hasMorePages: hasMorePages
});
});
} catch (exp) {
console.log(exp.message);
}
});
Here, we are first starting our server and connecting with MongoDB database. We created an API that accepts the page number as parameter. If the page number is not provided, then the default value of it will be 1. We are setting the limit to 1 for testing. We subtract 1 from current page value and multiply it by the limit, it will give use the number of records we need to skip.
Then we are fetching the data from database using skip and limit. Your document might have a lot of fields, so we created an obj to only return those fields that are need for this API. Finally, in order to check if there is a need for more pages, we first need to find the total number of records in our collection. We can then check if it is greater than the product of limit and page variable. If it is greater, it means there are still more records in the database.
That’s how you can create a “load more” button in React as your frontend and Node.js and MongoDB as your backend. If you face any problem in following this, kindly do let me know.