Having a bit of an issue regarding recursive functions that I can not wrap my head around and was wondering if I can get some help.
I have a json object like so below
const data = {
"navItems": [
{
"type": "directory",
"name": "Nav Title 1",
"children": [
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "File 2.pdf"
},
{
"type": "directory",
"name": "Sub Title 1",
"children": [
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub File 2.docx"
}
]
},
{
"type": "directory",
"name": "Sub Title 2",
"children": [
{
"type": "directory",
"name": "Sub Sub Title 1",
"children": [
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub Sub File 2.pdf"
}
]
},
{
"type": "directory",
"name": "Sub Sub Title 2",
"children": [
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub Sub File 2.pdf"
}
]
}
]
}
]
}
]
}
that I need to dynamically create a nav menu in react with this data set in a way it looks like this
let newNavItem = {
id: 'apps.' id,
title: item.name,
type: 'collapse',
icon: 'heroicons-outline:folder',
children: [
{
id: 'paper.' sub2item.name,
title: sub2item.name,
type: 'item',
url: sub2item.downloadUrl,
}
]
}
Example of a collapsible nav item (type = Directory)
{
id: id,
title: title,
type: 'collapse',
icon: 'heroicons-outline:folder',
children: []
}
Example of a clickable URL that would go inside the children array: (type = File)
{
id: id,
title: name,
type: 'item',
url: url,
}
EDIT: this is currently where I've gotten with the function.
const buildNavBlock = (navItem) => {
if (navItem.hasOwnProperty("children") && navItem.children.length > 0 && navItem.type == "directory") {
console.log(navItem.children);
return {
id: 'apps.' navItem.name,
title: navItem.name,
type: 'collapse',
icon: 'heroicons-outline:folder',
children: buildNavBlock(navItem.children)
}
} else {
return {
id: 'paper.' navItem.name,
title: navItem.name,
type: 'item',
url: 'apps/documents/',
}
}
};
data.navItems.forEach((item => {
let nav = buildNavBlock(item);
console.log(nav);
}))
CodePudding user response:
Seems when you are calling your recursive function you aren't calling it for each of the children. Changing it to this may help:
const buildNavBlock = (navItem) => {
if (navItem.hasOwnProperty("children") && navItem.children.length > 0 && navItem.type == "directory") {
console.log(navItem.children);
return {
id: 'apps.' navItem.name,
title: navItem.name,
type: 'collapse',
icon: 'heroicons-outline:folder',
children: navItem.children.map(child => buildNavBlock(child))
}
} else {
return {
id: 'paper.' navItem.name,
title: navItem.name,
type: 'item',
url: 'apps/documents/',
}
}
};
data.navItems.forEach((item => {
let nav = buildNavBlock(item);
console.log(nav);
}))
CodePudding user response:
it's might help
const data = {
"navItems": [
{
"type": "directory",
"name": "Nav Title 1",
"children": [
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "File 2.pdf"
},
{
"type": "directory",
"name": "Sub Title 1",
"children": [
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub File 2.docx"
}
]
},
{
"type": "directory",
"name": "Sub Title 2",
"children": [
{
"type": "directory",
"name": "Sub Sub Title 1",
"children": [
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub Sub File 2.pdf"
}
]
},
{
"type": "directory",
"name": "Sub Sub Title 2",
"children": [
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"name": "Sub Sub File 2.pdf"
}
]
}
]
}
]
}
]
}
/*let newNavItem = {
id: 'apps.' id,
title: item.name,
type: 'collapse',
icon: 'heroicons-outline:folder',
children: [
{
id: 'paper.' sub2item.name,
title: sub2item.name,
type: 'item',
url: sub2item.downloadUrl,
}
]
}*/
const {useEffect, useState} = React
const Dir=({data, isActive=true, collapsed=true})=>{
const [active,setActive]=useState(!collapsed)
return <li className={"dir " (isActive ? "active":"nested")} href="">
{data.title}
<span className={"caret " (active?"caret-down":"")} onClick={()=>setActive(v=>!v)}/>
<ul>
{(data.children || []).map(v=>v.type==="collapse"?
<Dir key={v.id} data={v} isActive={active}/>:<File key={v.id} data={v} isActive={active}/>)}
</ul>
</li>
}
const File=({data,isActive})=>{
return <li className={"file " (isActive ? "active":"nested")} href={data.url}>
{data.title}</li>
}
const App=(props)=>{
const makeNav=(data)=> (data.type === "directory")?
{
id: 'paper.' data.name,
title: data.name,
type: 'collapse',
icon: 'heroicons-outline:folder',
children: (data.children||[]).map(v=>makeNav(v))
}:(data.type === "file")?
{
id: 'paper.' data.name,
title: data.name,
type: 'item',
url: data.downloadUrl,
}:""
const [nav, setNav]=useState([])
useEffect(()=>{
let v=props.data.navItems.map(v=>makeNav(v))
setNav(v)
//console.log(v)
},[])
return <div >
<ul>
{nav.map(v=>v.type==="collapse"?
<Dir key={v.id} data={v}/>:<File key={v.id} data={v}/>)}
</ul>
</div>
}
ReactDOM.render(<App data={data}/>,document.getElementById('app'))
/* Style sidebar links */
.sidebar li {
padding: 6px 8px 6px 16px;
text-decoration: none;
font-size: 20px;
color: #818181;
}
/* Style links on mouse-over */
.sidebar li:hover {
color: #f1f1f1;
}
.caret {
cursor: pointer;
user-select: none; /* Prevent text selection */
}
/* Create the caret/arrow with a unicode, and style it */
.caret::before {
content: "\25B6";
color: black;
display: inline-block;
margin-right: 6px;
}
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
.caret-down::before {
transform: rotate(90deg);
}
/* Hide the nested list */
.nested {
display: none;
}
/* Show the nested list when the user clicks on the caret/arrow (with JavaScript) */
.active {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="app">
</div>