Comment sections are a fundamental feature in many web applications, fostering engagement and interaction among users. Implementing nested comment sections adds depth and organization to discussions, allowing for threaded conversations.
In modern web applications, engaging user interaction through comment sections is a vital aspect. One common feature is the ability to nest comments within replies, forming a hierarchical structure that allows for better organization and readability. In this tutorial, we'll explore how to build a nested comment section using React.
Getting Started
To begin, ensure you have Node.js and NPM installed on your system. We'll be using React, a popular JavaScript library for building user interfaces. Let's dive into the step-by-step process of creating a nested comment section.
Setting Up the React App
First, create a new React application using create-react-app
:
npx create-react-app nested-comments-app --template typescript
cd nested-comments-app
Next, navigate into the project directory and install any additional dependencies:
cd nested-comments-app
npm install
Implementing the Nested Comment Component
Within the React application, create a component named Comment
responsible for rendering individual comments and their nested replies. Here's an example of the Comment
component:
// Comment.tsx
import React from "react";
export interface CommentProps {
comment: {
id: string;
comment: string;
replies?: {
id: CommentProps["comment"]["id"];
comment: CommentProps["comment"]["comment"];
replies?: CommentProps["comment"]["replies"];
}[];
};
onReplyClick?: (id?: string) => void;
onReply?: (parentId: string, val: string) => void;
onDelete?: (parentId: string, val?: string) => void;
replyingID?: string;
}
export function Comment({
comment,
onReplyClick,
replyingID,
onReply,
onDelete,
}: CommentProps) {
return (
<ul>
<li>
<div style={{ display: "flex", alignItems: "center" }}>
<span>{comment.comment}</span>
</div>
{replyingID === comment.id ? (
<form
onSubmit={(ev) => {
ev.preventDefault();
const data = new FormData(ev.target as HTMLFormElement).get(
"comment"
);
if (data) {
onReply?.(comment.id, data as string);
}
}}
>
<input name="comment" />
<input type="submit" value="submit" />
<button onClick={() => onReplyClick?.()}>Cancel</button>
</form>
) : (
<>
<span onClick={() => onReplyClick?.(comment.id)}>reply</span>
<span>{" "}</span>
<span onClick={() => onDelete?.(comment.id, undefined)}>
Delete
</span>
</>
)}
{comment.replies?.map(
(reply) =>
reply && (
<Comment
key={reply.id}
comment={reply}
onReplyClick={onReplyClick}
replyingID={replyingID}
onReply={onReply}
onDelete={onDelete}
/>
)
)}
</li>
</ul>
);
}
Creating Nested Comments
Now, let's integrate the Comment
component into the App
component to display a list of nested comments:
// App.tsx
import React from "react";
import "./styles.css";
import { Comment, type CommentProps } from "./Comment";
function findNestedObj(
entireObj: any,
keyToFind: string,
valToFind: string,
message?: string,
isDelete?: boolean
) {
const uniqueId = (Math.random() * 100000).toString();
const foundObj = JSON.stringify(entireObj, (_, nestedValue) => {
if (nestedValue && nestedValue[keyToFind] === valToFind) {
if (isDelete) {
nestedValue = undefined;
} else {
if (nestedValue.replies) {
nestedValue.replies?.push({
id: uniqueId,
comment: message,
});
} else {
nestedValue.replies = [
{
id: uniqueId,
comment: message,
},
];
}
}
}
return nestedValue;
});
return JSON.parse(foundObj);
}
function App() {
const [comments, setComments] = React.useState<CommentProps["comment"][]>([
{
id: "1",
comment: "test1",
replies: [
{
id: "3",
comment: "test3",
replies: [
{
id: "5",
comment: "test5",
},
],
},
],
},
{
id: "2",
comment: "test2",
replies: [
{
id: "4",
comment: "test4",
},
],
},
]);
const [replyingId, setReplyingId] =
React.useState<CommentProps["comment"]["id"]>();
const attachReply: CommentProps["onReply"] = (parentId, comment) => {
const out = findNestedObj(comments, "id", parentId, comment);
setComments(out);
setReplyingId(undefined);
};
const deleteComment: CommentProps["onDelete"] = (parentId, comment) => {
const out = findNestedObj(comments, "id", parentId, comment, true);
setComments(out);
};
return (
<div className="App">
{comments.map(
(comment) =>
comment && (
<Comment
key={comment.id}
comment={comment}
onReplyClick={(id) => {
setReplyingId(id);
}}
replyingID={replyingId}
onReply={attachReply}
onDelete={deleteComment}
/>
)
)}
{!replyingId && <form
onSubmit={(ev) => {
ev.preventDefault();
const form = new FormData(ev.target as HTMLFormElement);
const data = form.get("new_comment");
if (data) {
setComments((prev) => {
const updated = [...prev];
updated.push({
id: (Math.random() * 100000).toString(),
comment: data as string,
});
(ev.target as HTMLFormElement).reset();
return updated;
});
}
}}
>
<input name="new_comment" />
<input type="submit" value="submit" />
</form>}
</div>
);
}
export default App;
Final Thoughts
Congratulations! You've successfully implemented a nested comment section using React. This structure allows users to interact with comments and replies hierarchically, enhancing the user experience within your web application.
Feel free to extend this functionality by adding features like comment submission, replying to specific comments, or integrating it with a backend service for data storage.
In conclusion, creating nested comment sections using React enables a more organized and user-friendly interface, fostering better engagement and communication among users.
I hope this guide helps you in implementing nested comment sections within your React applications. Happy coding!