How would i go about rendering a menu with nested <ul>
items with an an unknown amount of children in react from an object like in the following example?
[
{
title: "Top level 1",
slug: "top-level-1",
children: [
{
title: "Sub level 1",
slug: "sub-level-1",
children: [
{
title: "Sub Sub Level 1"
slug: "sub-sub-level-1"
}
]
}
{
title: "Sub level 2",
slug: "sub-level-2"
}
]
},
{
title: "Top level 2",
slug: "top-level 2"
}
]
How would i go about rendering a menu with nested <ul>
items with an an unknown amount of children in react from an object like in the following example?
[
{
title: "Top level 1",
slug: "top-level-1",
children: [
{
title: "Sub level 1",
slug: "sub-level-1",
children: [
{
title: "Sub Sub Level 1"
slug: "sub-sub-level-1"
}
]
}
{
title: "Sub level 2",
slug: "sub-level-2"
}
]
},
{
title: "Top level 2",
slug: "top-level 2"
}
]
Share
Improve this question
edited Nov 16, 2017 at 5:39
Shubham Khatri
282k58 gold badges430 silver badges411 bronze badges
asked Jul 24, 2017 at 17:35
SamuelSamuel
2,6356 gold badges34 silver badges41 bronze badges
4
- Two questions: 1. Have you attempted to do this by yourself? 2. Is the depth of nesting a known factor? – Kyle Richardson Commented Jul 24, 2017 at 17:38
-
yes i've tried a few different things. I'm able to render the top level items using
.map()
but i cannot seem to get my head around how to render the children. I would ideally like the solution to be able to render children regardless of depth – Samuel Commented Jul 24, 2017 at 17:41 -
If you want to be able to traverse an object of unknown depth you will need to use recursion. You'll want to make a function that takes an object and an accumulator as an argument. The function will check for
.children
on the provided object and invoke itself with each child object as an the argument, don't forget the accumulator! If the provided object does not have a .children
property it is a leaf node and you can generate you ponent with it's information. The accumulator is what the recursive function will used to build up the final collection as it goes. – Kyle Richardson Commented Jul 24, 2017 at 17:50 - thanks! that makes sense - i guess my real question is how would i write such a function that conditionally stores ponents/markup that i can then return later in my ponent. In vanilla js, i would just store the markup as strings then output - however this approach doesnt seem to work in react. – Samuel Commented Jul 24, 2017 at 18:08
2 Answers
Reset to default 14Codesandbox example
You just have to recursively call Menu ponent for its children to display and pass as a data prop.
let data = [
{
title: "Top level 1",
slug: "top-level-1",
children: [
{
title: "Sub level 1",
slug: "sub-level-1",
children: [
{
title: "Sub Sub Level 1",
slug: "sub-sub-level-1",
children: [
{
title: "Sub Sub Level 2",
slug: "sub-sub-level-2"
}
]
}
]
},
{
title: "Sub level 2",
slug: "sub-level-2"
}
]
},
{
title: "Top level 2",
slug: "top-level 2"
}
];
const Menu = ({data}) => {
return (
<ul>
{data.map(m => {
return (<li>
{m.title}
{m.children && <Menu data={m.children} />}
</li>);
})}
</ul>
);
}
const App = () => (
<div style={styles}>
<Hello name="CodeSandbox" />
<h2>Start editing to see some magic happen {'\u2728'}</h2>
<Menu data={data} />
</div>
);
You could recursively Render the ponent for nested data which has variable depth.
Sample Snippet.
var data = [
{
title: "Top level 1",
slug: "top-level-1",
children: [
{
title: "Sub level 1",
slug: "sub-level-1",
children: [
{
title: "Sub Sub Level 1",
slug: "sub-sub-level-1"
}
]
},
{
title: "Sub level 2",
slug: "sub-level-2"
}
]
},
{
title: "Top level 2",
slug: "top-level 2"
}
]
const MyComponent = (props) => {
if(Array.isArray(props.collection)) {
return <ul>
{props.collection.map((data)=> {
return <li>
<ul>
<li>{data.title}</li>
<li>{data.slug}</li>
<li><MyComponent collection={data.children}/></li>
</ul>
</li>
})
}
</ul>
}
return null;
}
class App extends React.Component {
render() {
return (
<MyComponent collection={data}/>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
P.S. The snippet contains formatting errors, but I am sure you will be able to rectify that. Snippet was to give an idea of the approach