Building a Nested Comments and Reply Component with React

In my recent job switch, the interviewer asked me to implement Nested Comments and reply functionality in ReactJS. Creating an interactive comment and reply functionality for a website can significantly enhance user engagement and provide a better user experience. In this interview article, we will explore how to implement a comment system where each comment has a text input, a reply button, and the ability to show or hide replies.

Requirements

  1. Comment Input and Reply Button: Each comment should have a text input field and a reply button.
  2. Display Children Comments: Display the comment’s children under each comment and each comment shall have a text area and a Reply button as specified in Step 1
  3. Reply to Comments: When the reply button is clicked, the entered text should be added as a child comment.
  4. Show/Hide Comments: Each comment should have a button to toggle the visibility of its child comments.

Comments Data:

The interviewer shared this Link(Click Here), mentioned copying the data in the URL, and asked to fetch the data by faking a promise.

const promise = new Promise((resolve, reject) => {
  resolve(data);
});

Here, the data is nothing but the data that I copied from the URL.

sample data

1. Setting Up the App Component

First, let’s create the main App component that will fetch comment data from the promise and render the Comments component.

import { useEffect, useState } from "react";
import "./styles.css";
import Comments from "./Comments";

const promise = new Promise((resolve, reject) => {
  resolve(data)
});

export default function App() {
  const [comments, setComments] = useState([]);

  useEffect(() => {
    promise
      .then((data) => {
        setComments(data);
      })
      .catch((error) => console.error("Error fetching:", error));
  }, []);

  return (
    <div className="App">
      <h1>Blog Comments</h1>
      <Comments com={comments} setComments={setComments} />
    </div>
  );
}

In this App component:

  • We use the useState hook to manage the comments data.
  • The useEffect hook is used to fetch data from the API when the component mounts.
  • The fetched data is passed to the Comments component via props.

2. Creating the Comments Component

The Comments component will handle the rendering of comments and their nested replies.

// Comments.js
import React, { useState } from "react";
import Reply from "./Reply";

const Comments = (props) => {
  const { com } = props;
  const [showComments, setShowComments] = useState(true);

  const addReplyToComment = (commentId, reply) => {
    // Find the comment in the comments state and add the reply
    const updatedComments = com.map((comment) => {
      if (comment._id === commentId) {
        return {
          ...comment,
          children: [...comment.children, reply],
        };
      }
      return comment;
    });

    // Update the comments state with the updated comments
    props.setComments(updatedComments);
  };

  return (
    <div>
      {com?.length > 0 &&
        com?.map((c) => {
          return (
            <div key={c._id}>
              <div className="comment">{c.comment}</div>
              <Reply commentId={c._id} addReplyToComment={addReplyToComment} />
              {c?.children?.length > 0 ? (
                <div className="reply">
                  <button onClick={() => setShowComments(!showComments)}>
                    {showComments ? "Hide Comments" : "Show Commnets"}
                  </button>
                  {showComments && <Comments com={c.children} />}
                </div>
              ) : null}
            </div>
          );
        })}
    </div>
  );
};

export default Comments;

In the Comments component:

  • The showComments state is used to toggle the visibility of nested comments.
  • The component maps through the comments array and renders each comment.
  • If a comment has children, a button to toggle the visibility of the child comments is displayed.

3. Adding a Reply Component

The Reply component provides a simple input field for users to type their replies.

// Reply.js
import React, { useState } from "react";

const Reply = (props) => {
  const { commentId, addReplyToComment } = props;
  const [reply, setReply] = useState("");

  const handleReplyChange = (e) => {
    setReply(e.target.value);
  };

  const handleReplySubmit = () => {
    // Create the reply object
    const newReply = {
      _id: Math.random().toString(36).substr(2, 9), // Generate a random ID
      comment: reply,
      children: [],
    };

    // Add the reply to the main comments state
    addReplyToComment(commentId, newReply);

    // Clear the reply input
    setReply("");
  };

  return (
    <div className="reply-container">
      <input
        type="text"
        value={reply}
        onChange={handleReplyChange}
        placeholder="Reply"
      />
      <button onClick={handleReplySubmit}>Submit Reply</button>
    </div>
  );
};

export default Reply;

We can style our components to look at least the bare minimum.

/* styles.css */

.comment {
  margin-bottom: 10px;
  padding: 10px;
  background-color: #f0f0f0;
}

.reply-container {
  margin-top: 10px;
}

.reply-container input[type="text"] {
  width: 200px;
  padding: 5px;
  margin-right: 10px;
}

.reply-container button {
  padding: 5px 10px;
  background-color: #007bff;
  color: #fff;
  border: none;
  cursor: pointer;
}

.reply-container button:hover {
  background-color: #0056b3;
}

After, putting it all together, this is how the whole Component will look like.

Building a Nested Comments and Reply Component with React

This example demonstrates fetching data, rendering comments and replies, and handling nested structures in React. Implementing nested comments in React involves managing state, rendering recursive components, and handling asynchronous data fetching. With this foundation, you can further enhance the functionality