我正在建立一个数据库,您可以在其中添加对会议的评论以及对这些会议的答复。我从this article获得了启发,因为我之前从未做过类似的事情。
我正在从数据库中获取评论,其结构如下所示:
"comments": [
{
"id": 5,
"content": "First comment",
"path": [
5
],
"depth": 1
},
{
"id": 6,
"content": "First reply on first comment",
"path": [
5,
6
],
"depth": 2
},
{
"id": 7,
"content": "Second comment",
"path": [
7
],
"depth": 1
},
{
"id": 8,
"content": "First reply on second comment",
"path": [
7,
8
],
"depth": 2
},
{
"id": 9,
"content": "Second reply on second comment",
"path": [
7,
9
],
"depth": 2
},
{
"id": 10,
"content": "First reply on second comment second reply",
"path": [
7,
9,
10
],
"depth": 3
}
]
当前,我正在尝试使用React应用程序创建一个嵌套列表,并以类似于StackOverflow的方式显示注释。我对结构的外观有所了解,但我不太确定如何实现它。
comments: [
{
id: 5,
content: "First comment",
replies: [
{
id: 6,
content: "First reply on first comment",
depth: 2
}
]
},
{
id: 100,
content: "Comment with no replies",
replies: []
}
]
在此方面的任何帮助都深表感谢,我希望我已经清楚地描述了我的问题。
感谢阅读,祝您愉快。
答案 0 :(得分:0)
我要做的第一件事是将parentId
,children
和order
添加到每个项目。无论您从哪个节点开始,这都可以使您上下遍历树并确保正确的排序。 parentId
是不言自明的,而children
应该被命名为childrenIds
,因为它只是以适当的顺序保存了孩子的ID。
然后,我通常喜欢将数组转换为以某些唯一值(例如ID)作为键的映射。这使您可以执行O(1)操作,以转到可能会被编辑或删除的任何节点。我喜欢将Ramda用于此类事情,但是您可以使用任何其他实用程序或编写自己的实用程序。
/**
* Convert an array to an object, using the `id` as the key.
*/
const keyById = R.indexBy(R.prop("id"));
现在,您需要找到列表顶部的注释,或者如果有注释,则在“根”注释下面。由于我选择使用parentId: null
来表示这一点,因此我们可以在其中找到这些顶级项。
const getTopLevelParents = R.filter(x => !x.parentId);
这样,我们可以轻松地获取顶级项目以开始迭代。这可能会使它变得更加复杂。我也在这里使用Ramda,所以我希望它对于理解不是很不寻常。要点是,从topLevel项中的第一个项开始,我们递归地找到子项,按“顺序”排序,设置深度,并将该项数组连接为单个数组。因此,结果是线性的并且排序正确。因为当我们将数据映射到组件时,我们不想为了找到父母和孩子而跳过。我们已经想通了所有这些,希望能够从上到下。
/**
* Converts the mapping to an array and reorders the children so they are directly adjacent with the depth set.
* @param treeMap Key value pair where the key is the ID and the value is the chapter
*/
function getTreeMapAsList(treeMap) {
const topLevel = R.compose(
getTopLevelParents,
R.values
)(treeMap);
/**
* Nest this function so it can have access to treeMap.
*/
function getTree(data, depth = 0) {
return R.reduce(
(result, curr) => {
result.push(curr);
// hasLength is a utility that just checks if the length of an
// item is greater than 0 in a way that won't blow up if something
// doesn't exist.
if (hasLength(R.prop("children", curr))) {
const children = R.compose(
R.sortBy(R.prop("order")),
R.reduce((result, id) => {
const child = R.prop(id, treeMap);
if (child) {
return R.compose(
R.append(R.__, result),
R.assoc("depth", depth + 1)
)(child);
}
return result;
}, [])
)(curr.children);
return result.concat(getTree(children, depth + 1));
}
return result;
},
[],
data
);
}
树现在已正确排序,具有适当的深度,并已展平为一个数组。最后,您可以使用注释组件在此数组上进行映射。
function Comment({ data }) {
return (
<div
style={{
//It's important to default to zero here. You could also
// just do this in the data or the mapper originally.
marginLeft: `${R.propOr(0, "depth", data)}rem`
}}
>
{data.content}
</div>
);
}
function App() {
// This is the map and should be stored in state somewhere.
// If it's changing a lot, you should memoize it.
const treeMap = keyById(data.comments);
// This is the array, that is now ordered with appropriate depths
// This should also be memoized.
const treeList = getTreeMapAsList(treeMap);
return (
<div>
<div>
{/* Now we just have an array to iterate over. */}
{treeList.map(item => {
return <Comment data={item} key={item.id} />;
})}
</div>
</div>
);
}
Here's a working example。祝你好运!