Soft Delete πŸ—‘ – Node.js, MongoDB

In this article, we will teach you, how you can implement soft delete in your Node.js app with MongoDB as database.

We also created a tutorial on how to do soft delete in Laravel, check our tutorial here.

Soft delete πŸ—‘ is useful when you do not want to actually delete the record from database, but mark the data as deleted.

You might have seen “This message has been deleted” on WhatsApp πŸ’¬ when you delete a message. That was soft delete. Because if you actually delete the message from database, then you would have no reference if there was a message at that time.

It is also helpful in cases where you want to allow the user to delete some data, but wants the administrator πŸ‘¨πŸ»β€πŸ’Ό to see the data. So when user deletes the data, it will not actually delete the data from database. It will simply hide it from user, but administrator can still see it. Only administrator can actually delete the data.

We will use “users” as entity for the sake of this tutorial.

In this article, you will learn:

  1. Insert users in MongoDB
  2. Fetch non-trashed users
  3. Soft delete a user
  4. Fetch trashed users
  5. Restore a user
  6. Permanently delete a user

1. Insert users in MongoDB βž•

Following API will insert a new document in MongoDB “users” collection using Node.js:

// API to insert data
app.post("/insert", async function (request, result) {
    // Get input fields
    const name = request.fields.name || "";

    // Insert data in database
    await db.collection("users")
        .insertOne({
            name: name,
            deletedAt: null // This will save the time when user was deleted
        });

    // Return response to the client
    result.json({
        status: "success",
        message: "User has been inserted."
    });
});

It will get user name from client app and insert a new document in “users” collection. Note that it creates another field “deletedAt” and set it’s value to null. Having NULL value means that the user is not deleted.

2. Fetch non-trashed users

Then we are going to fetch all the users that are not deleted.

// API to fetch non-trashed users
app.post("/fetch", async function (request, result) {
    const users = await db.collection("users")
        .find({
            deletedAt: null
        }).toArray();

    result.json({
        status: "success",
        message: "Users has been fetched.",
        users: users
    });
});

Here we are using filter to fetch all the records where deletedAt field is null.

3. Soft delete a user ␑

Now we will create an API that will soft delete a user.

// API to move the user to trash can
app.post("/delete", async function (request, result) {
    // Get user ID
    const _id = request.fields._id || "";

    // Don't delete the document,
    // just update the deletedAt field
    await db.collection("users")
        .updateOne({
            _id: ObjectId.createFromHexString(_id)
        }, {
            $set: {
                deletedAt: new Date()
            }
        });

    result.json({
        status: "success",
        message: "User has been moved to trash can."
    });
});

Here, we are first getting the user ID from client app. Then we are setting the deletedAt field to the current date.

If you run the /fetch API again, you will not see that user’s data in the response. Even though the record exists in the database, it will not be returned.

4. Fetch trashed users

Now that we have trashed the user, we need to find a way to see all those trashed users. So we will create an API that will return all the users that has been soft-deleted.

// API to see all trashed users
app.post("/trashed", async function (request, result) {
    const users = await db.collection("users")
        .find({
            deletedAt: {
                $ne: null
            }
        }).toArray();

    result.json({
        status: "success",
        message: "Trashed users has been fetched.",
        users: users
    });
});

At this point, you can give administrator 2 options, either:

  1. Restore the user
  2. Delete the user permanently

5. Restore a user ↩️

Restoring the user will simply set the deletedAt field of user back to null, like it was when user was created.

// API to restore a user
app.post("/restore", async function (request, result) {
    const _id = request.fields._id || "";

    await db.collection("users")
        .updateOne({
            _id: ObjectId.createFromHexString(_id)
        }, {
            $set: {
                deletedAt: null
            }
        });

    result.json({
        status: "success",
        message: "User has been restored."
    });
});

This will also receive a user ID that needs to be restored. If you call the /fetch API again, you will see that user in the response.

6. Permanently delete a user πŸ—‘

Permanently deleting a user will actually deletes the user from database. There will no way to restore it once done (unless you have a backup πŸŽ’ of your database).

// API to actually delete the user from database
app.post("/delete-permanently", async function (request, result) {
    const _id = request.fields._id || "";

    await db.collection("users")
        .deleteOne({
            $and: [{
                _id: ObjectId.createFromHexString(_id)
            }, {
                deletedAt: {
                    $ne: null
                }
            }]
        });

    result.json({
        status: "success",
        message: "User has been permanently deleted."
    });
});

Usually, this access is given to super admin only.

Bonus ⭐️

One more thing you can do, is if you have saved the user ID in other collections as well, then you can save the data in the dependent collections before removing them.

For example, if you have an “orders” collection, where you are saving user’s orders in the following format:

{
    "_id": ObjectId("1234567890"),
    "userId": ObjectId("0987654321")
}

If you permanently delete the user, then this order’s data will be lost as well. So best practice is to:

  1. Fetch the user.
  2. Put in orders collection.
  3. Delete the user.

Following is the code for that:

const user = await db.collection("users")
    .findOne({
        _id: ObjectId.createFromHexString(_id)
    });

if (user == null) {
    result.json({
        status: "error",
        message: "User not found."
    });
    return;
}

await db.collection("orders")
    .updateMany({
        userId: user._id
    }, {
        $set: {
            user: {
                _id: user._id,
                name: user.name
            }
        }
    });

Now, when you delete the user, your “orders” collection will save the user data for historical use.

{
    "_id": ObjectId("1234567890"),
    "userId": ObjectId("0987654321"),
    "user": {
        "_id": ObjectId("0987654321"),
        "name": "Adnan"
    }
}

Conclusion πŸ“

So that’s it. That’s how you can create a soft delete feature in your Node.js application with MongoDB as backend. It is very helpful in almost every project and I would recommend everyone must apply it. Whenever user delete something, soft delete it so if user done that by mistake, he will be able to recover it.

2 ways to loop through a number in React

In this article, we will discuss 2 ways how you can loop through a number in React.

While developing frontend of an application πŸ–₯, you might encounter a situation where you have to display a list of data. For example, a list of users etc.

Most common way to loop through an array in React is using Javascript built-in map function.

But if you have a number that you have to loop through. For example, run a loop till 10 etc.

In this case, we have 2 options.

1st way: Using function β˜‘οΈ

One way to loop through a number in React is to create a function that will:

  • Create an array.
  • Using basic for loop and push the HTML code in array.
  • Return the array.

Here is the code on how to do that. πŸ‘¨πŸ»β€πŸ’»

function App() {

    const [number, setNumber] = React.useState(10);

    function renderLoop() {
        const elements = [];

        for (let a = 0; a < number; a++) {
            elements.push(
                <p key={ `1st-way-${ a }` }>{ a }</p>
            );
        }

        return elements;
    }

    return (
        <>
            { renderLoop() }
        </>
    );
}

Following will be the output of above code:

0
1
2
3
4
5
6
7
8
9

2nd way: Using map βœ…

Javascript’s map function can also be used to iterate over an array and display a list without having to create a function.

function App() {

    const [number, setNumber] = React.useState(10);

    return (
        <>
            { Array.from({ length: number }, function (_, index) { return index })
                .map(function (n) {
                    return (
                        <p key={ `2nd-way-${ n }` }>{ n }</p>
                    )
            }) }
        </>
    );
}

Explanation πŸ•΅πŸ»β€β™‚οΈ

  • The first argument of Array.from is an object that will create an array of length 10 such that each element has an index starting from 0.
  • The second argument is a mapping function.
    • First parameter is “_” which means the value.
    • Second parameter is “index”.
    • It is returning the index without any modification, so the original number will be returned.
    • It will create an array from 0 to 9.
  • Using the map function, you can perform some action on that array created from number. In this case, we are displaying each number n in a paragraph. πŸ“„

So that’s how you can loop through a number in React. If you face any problem in following this, kindly do let me know. πŸ’¬

Get updated value from Child to Parent – React

In this tutorial, we will learn how you can get updated value from child component to parent component in React.

Let’s say you have a parent child component in your React app. You are sending data from parent to child and your child component is updating that variable’s value. Now when you get the value in parent component, you still get the original value, not the updated one.

First, you need to create a reference and pass it to the child component.

function Parent() {

    // Create a reference
    const childRef = React.useRef();

    // Create array of users
    const [users, setUsers] = React.useState([
        { id: 1, name: "Adnan" },
        { id: 2, name: "Afzal" }
    ]);

    // Function to get updated users
    function getUsers() {

        // Get users with reference
        let tempUsers = [];

        if (childRef.current) {
            tempUsers = childRef.current.getUsers();
        }

        console.log({
            "users": users,
            "tempUsers": tempUsers
        });
    }

    // Set child component and pass users array to it
    return (
        <>
            <Child users={ users }
                ref={ childRef } />

            {/* Button to get users */}

            <button type="button" onClick={ function () {
                getUsers();
            } }>Get users</button>
        </>
    );
}
  • childRef: First, we are creating a reference.
  • users: Then we are creating a state variable.
  • getUsers(): A function is created that will get the state value using reference.

We are passing the users array and reference to the child component.

A button is also created which when clicked, will call the function to get the state variable.

Then in your child component, you need to create it using forwardRef.

// Create child component that gets properties
// Export child using forward reference
const Child = React.forwardRef(function (props, ref) {

    // Get users from parent component
    const [users, setUsers] = React.useState(props.users || []);

    // Use imperative handler to return updated value
    React.useImperativeHandle(ref, function () {
        return {
            getUsers() {
                return users;
            }
        };
    });

    // Function to set user name to "Khalid" where user ID is 1
    function updateUser(id, name) {
        const tempUsers = JSON.parse(JSON.stringify(users));
        for (let a = 0; a < tempUsers.length; a++) {
            if (tempUsers[a].id == id) {
                tempUsers[a].name = name;
                break;
            }
        }
        setUsers(tempUsers);
    }

    // Create button to update user
    return (
        <>
            <button type="button" onClick={ function () {
                updateUser(1, "Khalid");
            } }>Update user</button>
        </>
    );
});

forwardRef: It is used to pass ref from parent component to child component.

Then creating a state variable and initialize it with props value received from parent component.

We are using imperative handler to return the updated value.

A button is created in child component that updates the value using it’s ID.

JSON.parse(JSON.stringify(users)): We are first converting the users array to JSON, then converting back to Javascript array. This ensures a deep copy.

That’s how you can get updated value from child component to parent component in React.

Next and previous links in PHP, MySQL

If you see a WordPress site, you will notice that at the bottom of the blog listing page, there will be links to next and previous pages.

They are useful to move the user to next or previous page.

If user is at the last page, then the next button will not be displayed.

Similarly, if user is at the first page, then the previous button will be hidden.

We are achieve this in PHP and MySQL. Following is the code to do that:

<?php

// Create a new PDO connection to the MySQL database
$pdo = new PDO("mysql:host=localhost; dbname=test", "root", "root", [
    PDO::ATTR_PERSISTENT => true // Enables persistent connection for better performance
]);

// Define the number of records to show per page
$per_page = 2;

// Get the current page number from the query string, default to 1 if not set
$page = $_GET["page"] ?? 1;

// Calculate the offset for the SQL query
$skip = ($page - 1) * $per_page;

// Fetch the users for the current page, ordered by ID in descending order
$sql = "SELECT * FROM users ORDER BY id DESC LIMIT $skip, $per_page";
$stmt = $pdo->prepare($sql); // Prepare the SQL statement
$stmt->execute([]); // Execute the query
$users = $stmt->fetchAll(PDO::FETCH_OBJ); // Fetch all results as objects

// Query to get the total number of users in the database
$sql = "SELECT COUNT(*) AS total_count FROM users";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_OBJ);
$total_count = $row->total_count ?? 0; // Get the total count or default to 0

// Calculate the total number of pages required
$total_pages = ceil($total_count / $per_page);

// Determine if there are more pages
$has_more_pages = ($page * $per_page) < $total_count;

// Initialize previous and next page variables
$previous_page = $next_page = null;

// Determine the previous page number
if ($page > 1)
{
    $previous_page = $page - 1;
}

// Determine the next page number
if ($page < $total_pages)
{
    $next_page = $page + 1;
}

?>

<!-- Loop through and display each user -->
<?php foreach ($users as $user): ?>
    <p><?php echo $user->name; ?></p>
<?php endforeach; ?>

<!-- Pagination controls -->
<?php if ($next_page > 0): ?>
    <a href="?page=<?php echo $next_page; ?>">Previous</a>
<?php endif; ?>

<?php if ($previous_page > 0): ?>
    <a href="?page=<?php echo $previous_page; ?>">Next</a>
<?php endif; ?>

Comments has been added with each line for explanation.

Even if you are working in any other language like Node.js and MongoDB, you can still achieve this because now you have the algorithm to do that.

We implemented this technique in one of our project: Multi-purpose platform in Node.js and MongoDB

From there, you will get the idea how you can render next and previous links if you are working in Node.js and MongoDB as backend and React as frontend.

Case-sensitive search in MySQL

Learn how to do case-sensitive search in MySQL. By default, MySQL search text-based column values without case sensitivity. For example, if your database looks like this:

Table: users

idemail
1support@adnan-tech.com
2support@Adnan-tech.com

And if you run the following query:

SELECT * FROM users WHERE email = "support@Adnan-tech.com";

It will return both records. However, it should have return only the 2nd record. The reason why, is because, usually the collation of text-based columns in MySQL is utf8_general_ci which is case insensitive.

So you just need to change the collation of “email” column in “users” table by running the following command:

ALTER TABLE users MODIFY email VARCHAR(255) COLLATE utf8mb4_bin;

If you run the query again, you will only see the 2nd row. Even though the 1st row value is also same, it is in lower-case and the query is in upper case “A”.

This is because utf8mb4_bin, it ensures that the search and sort on this column should be case sensitive.

That is how you can do case-sensitive search in MySQL using utf8mb4_bin collation. It is really helpful specially if you are searching on column that has UUID values.

Pagination in React, Node.js and MongoDB

In this tutorial, we will show you, how you can do pagination in your React app having Node.js and MongoDB as backend. Even if you have any other backend technology, you can still get the algorithm and apply it in your language.

React

Following is our React component that will call an AJAX request and fetch the data from API. After fetching the data from API, it will render the data along with the pagination links. Whenever any of the pagination link is clicked, it will refetch the data based on the clicked page number.

<script src="js/babel.min.js"></script>
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script> 

<div id="app"></div>

<script type="text/babel">

    function App() {

        // Set current page
        const [page, setPage] = React.useState(1);

        // Number of pages on pagination
        const [pages, setPages] = React.useState(0);

        // Whenever it's value is updated, the data will be re-fetched
        const [refetch, setRefetch] = React.useState(0);

        // 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);

                        // Update state variable
                        setUsers(response.users);

                        // Set if there are more pages
                        setHasMorePages(response.hasMorePages);

                        // Set pagination
                        setPages(response.pages);
                    }
                }
            };

            // 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();
        }, [refetch]);

        return (
            <>
                {/* Display data */}

                { users.map(function (user) {
                    return (
                        <p key={ `user-${ user._id }` }>{ user.name }</p>
                    );
                }) }

                {/* Show pagination if required */}
                
                { pages > 1 && (
                    <nav>
                        <ul>
                            { Array.from({ length: pages }, function (_, index) { return index + 1 } ).map(function (p) {
                                return (
                                    <li key={ `pagination-${ p }` } className={ `page-item ${ p == page ? "active" : "" }` }>
                                        <a href="#" onClick={ function (event) {
                                            event.preventDefault();
                                            setPage(p);
                                            setRefetch(refetch + 1);
                                        } }>{ p }</a>
                                    </li>
                                );
                            }) }
                        </ul>
                    </nav>
                ) }
            </>
        );

    }

    ReactDOM.createRoot(
        document.getElementById("app")
    ).render(<App />);

</script>

Node.js and MongoDB

Now in our Node.js server, we will create an API that will return all the records based on the current page from client side. It will also return the number of pagination links.

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/pagination-in-react-node-js-and-mongodb"));

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.");

        app.post("/fetch-users", async function (request, result) {
            const limit = 2;
            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;
			const pages = Math.ceil(totalCount / limit);

            result.json({
                status: "success",
                message: "Data has been fetched.",
                users: users,
                hasMorePages: hasMorePages,
                pages: pages
            });
        });

    } catch (exp) {
        console.log(exp.message);
    }
});

That’s how you can create pagination links in your React app with Node.js and MongoDB as backend. If you face any problem in following this, feel free to contact me.

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;

                    // ❌ Less efficient, more readable
                    // 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);*/

                    // βœ… Efficient way
                    setUsers([...users, ...newArr]); 

                    // 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.

Parse HTML entities in React

If you are working in React and are fetching data from an API that is returning some content with mixed HTML entities. You will notice that when you are rendering the HTML data in React, the HTML entities does not gets rendered properly. It is because React does not parse HTML entities by default.

Today, we will show you a way that let’s you parse HTML entities in React. First, let’s say we have a following React component that displays a text having an HTML entity.

function MyApp() {
	const [myVar, setMyVar] = React.useState("&copy;");

	return (
		<h1>Parse HTML entities in React: { myVar }</h1>
	)
}

Right now, it will output:

Parse HTML entities in React: &copy;

You can see that the &copy; HTML entity is not parsed properly. In order to parse it, we will use a library called html-react-parser. This library allows you to parse HTML to React. If you are developing your backend in Node.js, then this library can be used on both sides, frontend and backend.

You need to include the library in your project using it’s production link:

<script src="https://unpkg.com/html-react-parser@latest/dist/html-react-parser.min.js"></script>

You can also download this JS file and paste in your project and use relative path. Finally, you need to call the “HTMLReactParser(var string)” function and send your variable as an argument.

{ HTMLReactParser(myVar) }

If you run the page now, you will see that your HTML entity has been parsed properly.

Insert new HTML without re-rendering

If you are looking for a way to insert new HTML without re-rendering the previous HTML of the node, you have come to the right place. So let’s say you are working in a Javascript application and are rendering new data using innerHTML, we will show you a better way i.e. insertAdjacentHTML. insertAdjacentHTML method accepts a specific position as first argument and HTML text as second argument and insert the HTML as DOM (Document Object Model) at that position.

Why insert new HTML in DOM ?

If you are a frontend developer, there will be times when you need to insert new HTML to existing DOM. For example, in pagination, the data is loaded first. After that, when user clicks on “load more” button, it fetch more data from API and append it. Or if you are working in a chat application, you can see that the previous messages are prepended when you scroll to the top of conversation.

Acceptable positions are:

  • beforebegin: It will insert the HTML before element. So the new HTML will become a previous sibling of that element.
  • afterbegin: This will add the HTML as the first child of the element.
  • beforeend: This will insert HTML as the last child of that element.
  • afterend: And this will add the HTML after the element. So the new HTML will become a next sibling of that element.

Here are simple examples.

beforebegin

This is usefult if you want to prepend the element before a specific node.

<div id="target">I am target.</div>

<script>
    const html = "<p>I am new HTML</p>";
    const target = document.getElementById("target");
    target.insertAdjacentHTML("beforebegin", html);
</script>

Output of this will be:

I am new HTML

I am target.

And your DOM will look like this:

 <p>I am new HTML</p>
<div id="target">I am target.</div>

The paragraph tag became the previous sibling of div tag.

afterbegin

This will add the new HTML as the first child of target node. Change the above line #6 to:

target.insertAdjacentHTML("afterbegin", html);

It’s output will look same but now your DOM will be like this:

<div id="target">
    <p>I am new HTML</p>
    I am target.
</div>

You can see how the paragraph tag became the first child of div tag. The equivalent of this in innerHTML will be:

target.innerHTML = html + target.innerHTML;

But I will explain later why using innerHTML to insert new HTML is a bad idea.

beforeend

If you use “beforeend”, it will add the new HTML as the last child of that element. Change that line again to:

target.insertAdjacentHTML("beforeend", html);

This time, your output will be:

I am target.

I am new HTML

And your DOM will become:

<div id="target">
    I am target.
    <p>I am new HTML</p>
</div>

afterend

This will insert the HTML right after the element, making it the next sibling of target node.

target.insertAdjacentHTML("afterend", html);

Your output will be same as in “beforeend” but your DOM will become:

<div id="target">I am target.</div>
<p>I am new HTML</p>

When to use insertAdjacentHTML over innerHTML ?

innerHTML removes all the event listeners previously attached to the nodes. So if you have a button that has an onclick event listener, after re-rendering it using innerHTML will remove that event. But insertAdjacentHTML keeps the event listeners as they are.

One of the most important difference is performance. innerHTML re-renders the whole node again, while insertAdjacentHTML only insert the new HTML without re-rendering the previous HTML.

When to use innerHTML over insertAdjacentHTML ?

You should use innerHTML only when you have to completely re-write a node. For example, changing the text of a button. If there is any new element that needs to inserted, prepended or appended, it should be done using insertAdjacentHTML.

Callback function in React JS

In this article, we will teach you how you can use a callback function to be called from child parent that will invoke a function in parent component in React JS.

Let’s say you have:

  • A parent component that has an array.
  • You are looping through an array and displaying the data using child component.
  • You want to call a function from child component that will invoke a function in parent component.
<html>
    <head>
        <script src="js/babel.min.js"></script>
        <script src="js/react.development.js"></script>
        <script src="js/react-dom.development.js"></script>

        <script type="text/babel" src="js/SingleData.js"></script>
    </head>

    <body>
        <div id="app"></div>

        <script type="text/babel">
            function App() {

                const [data, setData] = React.useState([{
                    id: 1,
                    name: "Adnan",
                    age: 31
                }, {
                    id: 2,
                    name: "Afzal",
                    age: 65
                }, {
                    id: 3,
                    name: "Ali",
                    age: 43
                }]);

                return (
                    <>
                        { data.map(function (d, index) {
                            return (
                                <SingleData key={ `data-${ d.id }` }
                                    d={ d } />
                            );
                        }) }
                    </>
                );
            }

            ReactDOM.createRoot(
                document.getElementById("app")
            ).render(<App />);
        </script>
    </body>
</html>

Here, we have a React parent component that has an array of data. Each array element has an ID, name and age. You might be receiving this data from API. In each loop iteration, it is rendering a child component.

  • key: This is used by React JS to uniquely identify an element.
  • d: This is the property value parent component is sending to the child component.

What are props in React JS ?

props (properties): Props are used in React JS to pass the data from parent component to child component. They are read-only so child component cannot modify the value of “d”.

Our child component will look like this:

// js/SingleData.js

function SingleData({ d }) {
    return (
        <div>
            <p>
                { d.id } - { d.name } { d.age }
            </p>
        </div>
    );
}

Callback function

Now, let’s assume you want to have a delete function in child component that, when called, will call a function in parent component. So that the parent component that perform an action on that child. In this case, we will remove that array element from “data” array in parent component.

So first, we will create a delete button in child parent. Once that button is clicked, we will call a function we will receive in props from parent component.

// js/SingleData.js

function SingleData({ d, onDelete }) {
    return (
        <div>
            <p>
                { d.id } - { d.name } { d.age }
            </p>

            <button type="button"
                onClick={ function () {
                    onDelete(d.id);
                } }>Delete</button>
        </div>
    );
}

Now, in our parent component, we need to pass this function as an argument to the child component.

<SingleData key={ `data-${ d.id }` }
    d={ d }
    onDelete={ onDelete } />

Finally, we will create that function in parent component that will search the array element using ID and remove it from array. At the end, we will update the React state variable as well.

function onDelete(id) {
    const tempArr = [ ...data ];
    for (let a = 0; a < tempArr.length; a++) {
        if (tempArr[a].id == id) {
            tempArr.splice(a, 1);
            break;
        }
    }

    setData(tempArr);
}

Here is the complete code of this tutorial.

Parent component

<html>
    <head>
        <script src="js/babel.min.js"></script>
        <script src="js/react.development.js"></script>
        <script src="js/react-dom.development.js"></script>

        <script type="text/babel" src="js/SingleData.js"></script>
    </head>

    <body>
        <div id="app"></div>

        <script type="text/babel">
            function App() {

                const [data, setData] = React.useState([{
                    id: 1,
                    name: "Adnan",
                    age: 31
                }, {
                    id: 2,
                    name: "Afzal",
                    age: 65
                }, {
                    id: 3,
                    name: "Ali",
                    age: 43
                }]);

                function onDelete(id) {
                    const tempArr = [ ...data ];
                    for (let a = 0; a < tempArr.length; a++) {
                        if (tempArr[a].id == id) {
                            tempArr.splice(a, 1);
                            break;
                        }
                    }

                    setData(tempArr);
                }

                return (
                    <>
                        { data.map(function (d, index) {
                            return (
                                <SingleData key={ `data-${ d.id }` }
                                    d={ d }
                                    onDelete={ onDelete } />
                            );
                        }) }
                    </>
                );
            }

            ReactDOM.createRoot(
                document.getElementById("app")
            ).render(<App />);
        </script>
    </body>
</html>

Child component

function SingleData({ d, onDelete }) {
    return (
        <div>
            <p>
                { d.id } - { d.name } { d.age }
            </p>

            <button type="button"
                onClick={ function () {
                    onDelete(d.id);
                } }>Delete</button>
        </div>
    );
}

We used this method in one of our project “Multi-purpose platform in Node JS and Mongo DB“. You can check this out if you want to get a practically view of it.

That is how you can use a callback function in React JS that will invoke a function from child component to parent component.