Javascript 如何在React中创建动态嵌套手风琴
我怎样才能创作出这样的手风琴Javascript 如何在React中创建动态嵌套手风琴,javascript,reactjs,accordion,Javascript,Reactjs,Accordion,我怎样才能创作出这样的手风琴 -parent -subparent1 -subparent2 ... -subparentN - child 这是我的数据 //parents {id: 1, name: "", parent_id: null} {id: 2, name: "", parent_id: null } {id: 3, name: "", pa
-parent
-subparent1
-subparent2
...
-subparentN
- child
这是我的数据
//parents
{id: 1, name: "", parent_id: null}
{id: 2, name: "", parent_id: null }
{id: 3, name: "", parent_id: null }
{id: 4, name: "", parent_id: null}
//children
{id: 5, name: "", parent_id: 1}
{id: 6, name: "", parent_id: 1}
{id: 7, name: "", parent_id: 5}
{id: 8, name: "", parent_id: 5}
{id: 9, name: "", parent_id: 6}
{id: 10, name: "", parent_id: 6}
{id: 11, name: "", parent_id: 6}
{id: 12, name: "", parent_id: 6}
{id: 13,name: "", parent_id: 6}
{id: 14, name: "", parent_id: 6}
基本上,那些有parent_id:null的人是父母,当我点击他们时,我希望他们的潜在子女显示出来,如果他们有,现在这并不难,但我不明白的是如何显示子父母的子女,我认为您的数据结构有缺陷。除了父子关系之外,您还应该跟踪父子关系。现在,您将能够轻松地遍历数据并渲染子父级的子级
{id:1,父\u id:null,子项:[
{id:2,父\u id:1,子:[]},
{id:3,父\子id:1,子:[
{id:4,父\u id:3,子:[]}
]}
]}
如果需要使所有对象保持内联,可以按如下方式构造数据:
{id:1,父\u id:null,子:[2,3]}
{id:2,父\u id:1,子:[]},
{id:3,父\子id:1,子:[4]},
{id:4,父\u id:3,子:[]}
您可以循环查看所有项目的列表,并将每个子项目添加到其父项中。之后,您只需循环数组中的所有项并创建它们各自的html
const项=[
//父母
{id:1,名称:“1”,父\u id:null},
{id:2,名称:“2”,父\u id:null},
{id:3,名称:“3”,父\u id:null},
{id:4,名称:“4”,父\u id:null},
//孩子们
{id:5,名称:“5”,父id:1},
{id:6,名字:“6”,父id:1},
{id:7,姓名:“7”,家长id:5},
{id:8,姓名:“8”,家长id:5},
{id:9,姓名:“9”,家长id:6},
{id:10,姓名:“10”,家长id:6},
{id:11,姓名:“11”,家长id:6},
{id:12,姓名:“12”,家长id:6},
{id:13,姓名:“13”,家长id:6},
{id:14,姓名:“14”,家长id:6},
];
用于(项目的常数项){
//查找父对象
const parent=items.find(({id})=>id==item.parent\u id);
//如果找到父对象,则将该对象添加到其子数组中
如果(家长){
parent.children=parent.children?[…parent.children,item]:[item]
}
};
//仅在主数组中保留根元素(父元素)
const list=items.filter({parent\u id})=>!parent\u id);
//控制台日志(列表);
//显示树(vanillaJS,无反应)
用于(列表中的常量项){
//为每个项目创建一个新分支
const ul=创建分支(项目);
//将分支追加到文档中
文件.正文.附件(ul);
}
函数createBranch(项){
//为每个分支创建ul项目
const ul=document.createElement(“ul”);
//将当前项目作为li添加到分支
const li=document.createElement(“li”);
li.textContent=item.name;
ul.儿童(li);
//检查是否有孩子
if(项目.子项){
//为每个子级创建一个新分支
for(项的常量子项。子项){
const subUl=createBranch(子级);
//将子分支追加到当前分支
ul.儿童(subUl);
}
}
返回ul;
}
ul{
保证金:0;
左:2rem;
}
我认为您的数据结构应该是一个嵌套对象,类似于您看到菜单工作的方式,即
[
{id: 1, name:"", children: [
{id: 5, name: "", children: []},
{id: 6, name: "", children: [
{id: 7, name: "", children: []},
{id: 8, name: "", children: []},
]},
]},
{id: 2, name:"", children:[]}
]
然后需要一个函数来输出每个项目:
const returnMenuItem = (item, i) =>{
let menuItem;
if (item.children.length===0) {
menuItem = <div key={i}>{item.label}</div>;
}
else {
let menuItemChildren = item.children.map((item,i)=>{
let menuItem = returnMenuItem(item,i);
return menuItem;
});
menuItem = <div key={i}>
<div>{item.label}</div>
<div>
{menuItemChildren}
</div>
</div>;
}
return menuItem;
}
一个完整的组件看起来如下所示:
import React, { useState, useEffect } from "react";
import { UncontrolledCollapse } from "reactstrap";
const Menu = (props) => {
const [loading, setLoading] = useState(true);
const [items, setItems] = useState([]);
useEffect(() => {
const menuData = [
{
id: 1,
name: "test 1",
children: [
{ id: 5, name: "test 5", children: [] },
{
id: 6,
name: "test 6",
children: [
{ id: 7, name: "test 7", children: [] },
{ id: 8, name: "test 8", children: [] }
]
}
]
},
{ id: 2, name: "test 2", children: [] }
];
const returnMenuItem = (item, i) => {
let menuItem;
if (item.children.length === 0) {
menuItem = (
<div className="item" key={i}>
{item.name}
</div>
);
} else {
let menuItemChildren = item.children.map((item, i) => {
let menuItem = returnMenuItem(item, i);
return menuItem;
});
menuItem = (
<div key={i} className="item">
<div className="toggler" id={`toggle-menu-item-${item.id}`}>
{item.name}
</div>
<UncontrolledCollapse
className="children"
toggler={`#toggle-menu-item-${item.id}`}
>
{menuItemChildren}
</UncontrolledCollapse>
</div>
);
}
return menuItem;
};
const load = async () => {
setLoading(false);
let menuItems = menuData.map((item, i) => {
let menuItem = returnMenuItem(item, i);
return menuItem;
});
setItems(menuItems);
};
if (loading) {
load();
}
}, [loading]);
return <div className="items">{items}</div>;
};
export default Menu;
你可以在这里找到一个工作的代码沙盒创建子家长作为一个组件,并拥有自己的打开/关闭状态,然后点击传播,应该和你为家长所做的一样你是一个绝对的野兽,真正的天才,非常感谢你,你是一个救命恩人。我自己从来没有接近过这一点。顺便说一句,我有另一层的孩子,它的工作原理都一样,再次感谢!您好,回答得很好,如果我想显示所有元素,它会起作用,但我如何做手风琴之类的事情呢?我想按下父节点,让子节点折叠。我通常使用react引导包作为react引导包。然后,我将菜单包装在Nav()中,并将包含子菜单项的菜单项包装在UncontrolledDropdown元素()中。最后,对于项目,我使用NavItem元素。如果你想使用手风琴的方法,你可以用折叠()的方式将你的物品和子项包装起来。如果您觉得这很困难,或者不确定如何使用,请详细描述预期行为,我可以扩展我的答案以涵盖这一点。是的,请。我不知道如何使用您提供的returnMenuItem函数。非常感谢,所有这些嵌套和递归让我用一个示例组件更新了答案。您现在应该拥有根据需要构建组件所需的一切。干杯,随时乐意提供帮助。如果这是你想要的,那么你应该检查这是正确的答案。
import React, { useState, useEffect } from "react";
import { UncontrolledCollapse } from "reactstrap";
const Menu = (props) => {
const [loading, setLoading] = useState(true);
const [items, setItems] = useState([]);
useEffect(() => {
const menuData = [
{
id: 1,
name: "test 1",
children: [
{ id: 5, name: "test 5", children: [] },
{
id: 6,
name: "test 6",
children: [
{ id: 7, name: "test 7", children: [] },
{ id: 8, name: "test 8", children: [] }
]
}
]
},
{ id: 2, name: "test 2", children: [] }
];
const returnMenuItem = (item, i) => {
let menuItem;
if (item.children.length === 0) {
menuItem = (
<div className="item" key={i}>
{item.name}
</div>
);
} else {
let menuItemChildren = item.children.map((item, i) => {
let menuItem = returnMenuItem(item, i);
return menuItem;
});
menuItem = (
<div key={i} className="item">
<div className="toggler" id={`toggle-menu-item-${item.id}`}>
{item.name}
</div>
<UncontrolledCollapse
className="children"
toggler={`#toggle-menu-item-${item.id}`}
>
{menuItemChildren}
</UncontrolledCollapse>
</div>
);
}
return menuItem;
};
const load = async () => {
setLoading(false);
let menuItems = menuData.map((item, i) => {
let menuItem = returnMenuItem(item, i);
return menuItem;
});
setItems(menuItems);
};
if (loading) {
load();
}
}, [loading]);
return <div className="items">{items}</div>;
};
export default Menu;
.item {
display: block;
}
.item > .children {
padding: 0 0 0 40px;
}
.item > .toggler {
display: inline-block;
}
.item::before {
content: "-";
padding: 0 5px 0 0;
}