Given the below array, I'd like to render comments
in a threaded manner by making use of parentId
.
comments: [
{
id: 1,
parentId: null
},
{
id: 2,
parentId: 1
},
{
id: 3,
parentId: 1
},
{
id: 4,
parentId: 3
},
{
id: 5,
parentId: 4
}
]
I thought with the below components I'd be able to recurse through the comments, but the output is not what I'd expect (it seems to be rendering a new <ul>
element for every comment.) I'm a bit new to react and javascript, so maybe I'm not implementing the recursion correctly, or should comments
be structured differently?
const Comment = (props) => (
<li>
{props.comment.id}
{props.comment.children.length > 0 ?
<Comments comments={props.comment.children}/>
: null }
</li>
);
const Comments = (props) => (
<ul>
{props.comments.map((comment) => {
comment.children = _.filter(props.comments, {'parentId': comment.id});
return <Comment key={comment.id} comment={comment}/>
})}
</ul>
);
If you turn that list into a structure which actually reflects the nested hierarchy of the comments, then you'll have an easier time building a component for rendering them.
[
{
id: 1,
children: [
{ id: 2, children: [] },
{ id: 3, children: [ ... ] }
]
}
]
You could implement a function to do the conversion.
function nestComments(commentList) {
const commentMap = {};
// move all the comments into a map of id => comment
commentList.forEach(comment => commentMap[comment.id] = comment);
// iterate over the comments again and correctly nest the children
commentList.forEach(comment => {
if(comment.parentId !== null) {
const parent = commentMap[comment.parentId];
(parent.children = parent.children || []).push(comment);
}
});
// filter the list to return a list of correctly nested comments
return commentList.filter(comment => {
return comment.parentId === null;
});
}
Here's an idea for how you could go from that flat structure to a list of nested comments. Once you're done with that implementation, all you'd need would be a recursive React component.
function Comment({ comment }) {
const nestedComments = (comment.children || []).map(comment => {
return <Comment comment={comment} />;
});
return (
<div key={comment.id}>
<span>{comment.text}</span>
<a href={comment.author.url}>{comment.author.name}</a>
{nestedComments}
</div>
);
}
in case you need an example that needs to go unknown levels deep i solved with this
function Comment({text, author}){
return <div>{author}: {text}</div>
}
CommentTree(comments) {
let items = comments.map((comment) => {
return (
<div className="border-l pl-6">
<Comment
key={comment.id}
text={comment.text}
author={comment.author}
/>
{comment.children && CommentTree(comment.children)}
</div>
)
})
return items
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With