What is this post about ?
Hello fellow humanoids. Today we will try to implement a basic nested comments component in React Js. This post won't be focused much on the styling rather the bare minimum logic required.
Check out the app here : Nested Comment
Content
- How to structure a comment
- How to fetch all comments
- React Comment component
- Adding new comment
Lets go deep dive into each one and explore how it was implemented.
How to structure a comment
For this implementation we would be using flat structure to store all comments. This way fetching comment based on ID will be quicker.
const getNewComment = (commentValue, isRootNode = false, parentNodeId) => { | |
return { | |
id: uuidv4(), | |
commentText: commentValue, | |
childCommments: [], | |
isRootNode, | |
parentNodeId, | |
}; | |
}; |
comments = {
id1 : comment1,
id2 : comment2
}
How to fetch all comments
In this case we would enhance the existing comments from flat structure to nested structure so it can be used to render nested comments.
const commentMapper = (comment) => { | |
return { | |
...comment, | |
childCommments: comment.childCommments | |
.map((id) => comments[id]) | |
.map((comment) => commentMapper(comment)), | |
}; | |
}; | |
const enhancedComments = Object.values(comments) | |
.filter((comment) => { | |
return !comment.parentNodeId; | |
}) | |
.map(commentMapper); |
React Comment component
Let's create a basic comment component with basic utility functions
const Comment = ({ comment, addComment }) => { | |
const { commentText, childCommments, id } = comment; | |
const [childComment, setChildComment] = useState(""); | |
const [show, setShow] = useState(true); | |
const [showAddComponet, setShowAddComponet] = useState(false); | |
const onAdd = () => { | |
addComment(id, childComment); | |
setChildComment(""); | |
setShowAddComponet(false); | |
}; | |
return ( | |
<div className="Comment"> | |
<div | |
style={{ | |
display: "flex", | |
flexDirection: "row", | |
justifyContent: "space-between", | |
}} | |
> | |
<div style={{ textAlign: "left" }}>{commentText}</div> | |
| |
{childCommments.length > 0 && ( | |
<button onClick={() => setShow((show) => !show)}> | |
{show ? "Hide" : "Show"} | |
</button> | |
)} | |
</div> | |
<div> | |
<div> | |
{showAddComponet ? ( | |
<> | |
<input | |
type="text" | |
value={childComment} | |
onChange={(e) => setChildComment(e.target.value)} | |
placeholder="add comment" | |
/>{" "} | |
<button onClick={onAdd}>Submit</button> | |
</> | |
) : ( | |
<a | |
style={{ cursor: "pointer", fontSize: "0.7rem", color: "blue" }} | |
onClick={() => setShowAddComponet(true)} | |
> | |
Add a reply | |
</a> | |
)} | |
</div> | |
</div> | |
{show && | |
childCommments.map((childCommentEl, key) => { | |
return ( | |
<Comment | |
key={key} | |
comment={childCommentEl} | |
addComment={addComment} | |
/> | |
); | |
})} | |
</div> | |
); | |
}; |
Adding new comment
Once a new comment is added, based on the parentNodeId we can update the parent node and add a new comment to the comments list.
function App() { | |
const [comments, setComments] = useState(initialState); | |
const [rootComment, setRootComment] = useState(""); | |
const addComment = (parentId, newCommentText) => { | |
let newComment = null; | |
if (parentId) { | |
newComment = getNewComment(newCommentText, false, parentId); | |
setComments((comments) => ({ | |
...comments, | |
[parentId]: { | |
...comments[parentId], | |
childCommments: [...comments[parentId].childCommments, newComment.id], | |
}, | |
})); | |
} else { | |
newComment = getNewComment(newCommentText, true, null); | |
} | |
setComments((comments) => ({ ...comments, [newComment.id]: newComment })); | |
}; | |
const commentMapper = (comment) => { | |
return { | |
...comment, | |
childCommments: comment.childCommments | |
.map((id) => comments[id]) | |
.map((comment) => commentMapper(comment)), | |
}; | |
}; | |
const enhancedComments = Object.values(comments) | |
.filter((comment) => { | |
return !comment.parentNodeId; | |
}) | |
.map(commentMapper); | |
const onAdd = () => { | |
addComment(null, rootComment); | |
setRootComment(""); | |
}; | |
return ( | |
<div className="App"> | |
<header style={{marginBottom : '2rem', fontSize : '2rem'}}>Nested Comment Example</header> | |
<div className="comments-container"> | |
<input | |
type="text" | |
value={rootComment} | |
onChange={(e) => setRootComment(e.target.value)} | |
placeholder="add comment" | |
style={{ width: "80%", marginRight: "1rem" }} | |
/>{" "} | |
<button onClick={onAdd}>Add</button> | |
</div> | |
<div | |
style={{ | |
border: "1px solid blue", | |
width: "60%", | |
margin: "auto", | |
overflowX: "auto", | |
padding: "2rem", | |
}} | |
> | |
{enhancedComments.map((comment, key) => { | |
return ( | |
<Comment key={key} comment={comment} addComment={addComment} /> | |
); | |
})} | |
</div> | |
</div> | |
); | |
} |
Conclusion
This app was made as part of learning new components which are used in real life applications.
Stay safe and lend a hand to another :)
Top comments (0)