I am making a menu and submenus using recursion function
and I am in the need of help to open only the respective menu and sub menu's..
For button and collapse Reactstrap has been used..
Recursive function that did menu population:
{this.state.menuItems &&
this.state.menuItems.map((item, index) => {
return (
<div key={item.id}>
<Button onClick={this.toggle.bind(this)}> {item.name} </Button>
<Collapse isOpen={this.state.isToggleOpen}>
{this.buildMenu(item.children)}
</Collapse>
</div>
);
})}
And the buildMenu
function as follows,
buildMenu(items) {
return (
<ul>
{items &&
items.map(item => (
<li key={item.id}>
<div>
{this.state.isToggleOpen}
<Button onClick={this.toggle.bind(this)}> {item.name} </Button>
<Collapse isOpen={this.state.isToggleOpen}>
{item.children && item.children.length > 0
? this.buildMenu(item.children)
: null}
</Collapse>
</div>
</li>
))}
</ul>
);
}
There is no problem with the code as of now but I am in the need of help to make menu -> submenu -> submenu
step by step open and closing respective levels.
Working example: https://codesandbox.io/s/reactstrap-accordion-9epsp
You can take a look at this example that when you click on any menu the whole level of menus gets opened instead of clicked one..
Requirement
If user clicked on menu One
, then the submenu (children)
-> One-One
needs to get opened.
And then if user clicked on One-One
,
-> One-One-One
-> One - one - two
-> One - one - three
needs to get opened.
Likewise it is nested so after click on any menu/ children their respective next level needs to get opened.
I am new in react and reactstrap way of design , So any help from expertise would be useful for me to proceed and learn how actually it needs to be done.
Instead of using one large component, consider splitting up your component into smaller once. This way you can add state to each menu item to toggle the underlying menu items.
If you want to reset al underlying menu items to their default closed position you should create a new component instance each time you open up a the underlying buttons. By having <MenuItemContainer key={timesOpened}
the MenuItemContainer
will be assigned a new key when you "open" the MenuItem
. Assigning a new key will create a new component instance rather than updating the existing one.
For a detailed explanation I suggest reading You Probably Don't Need Derived State - Recommendation: Fully uncontrolled component with a key
.
const loadMenu = () => Promise.resolve([{id:"1",name:"One",children:[{id:"1.1",name:"One - one",children:[{id:"1.1.1",name:"One - one - one"},{id:"1.1.2",name:"One - one - two"},{id:"1.1.3",name:"One - one - three"}]}]},{id:"2",name:"Two",children:[{id:"2.1",name:"Two - one"}]},{id:"3",name:"Three",children:[{id:"3.1",name:"Three - one",children:[{id:"3.1.1",name:"Three - one - one",children:[{id:"3.1.1.1",name:"Three - one - one - one",children:[{id:"3.1.1.1.1",name:"Three - one - one - one - one"}]}]}]}]},{id:"4",name:"Four"},{id:"5",name:"Five",children:[{id:"5.1",name:"Five - one"},{id:"5.2",name:"Five - two"},{id:"5.3",name:"Five - three"},{id:"5.4",name:"Five - four"}]},{id:"6",name:"Six"}]);
const {Component, Fragment} = React;
const {Button, Collapse} = Reactstrap;
class Menu extends Component {
constructor(props) {
super(props);
this.state = {menuItems: []};
}
render() {
const {menuItems} = this.state;
return <MenuItemContainer menuItems={menuItems} />;
}
componentDidMount() {
loadMenu().then(menuItems => this.setState({menuItems}));
}
}
class MenuItemContainer extends Component {
render() {
const {menuItems} = this.props;
if (!menuItems.length) return null;
return <ul>{menuItems.map(this.renderMenuItem)}</ul>;
}
renderMenuItem(menuItem) {
const {id} = menuItem;
return <li key={id}><MenuItem {...menuItem} /></li>;
}
}
MenuItemContainer.defaultProps = {menuItems: []};
class MenuItem extends Component {
constructor(props) {
super(props);
this.state = {isOpen: false, timesOpened: 0};
this.open = this.open.bind(this);
this.close = this.close.bind(this);
}
render() {
const {name, children} = this.props;
const {isOpen, timesOpened} = this.state;
return (
<Fragment>
<Button onClick={isOpen ? this.close : this.open}>{name}</Button>
<Collapse isOpen={isOpen}>
<MenuItemContainer key={timesOpened} menuItems={children} />
</Collapse>
</Fragment>
);
}
open() {
this.setState(({timesOpened}) => ({
isOpen: true,
timesOpened: timesOpened + 1,
}));
}
close() {
this.setState({isOpen: false});
}
}
ReactDOM.render(<Menu />, document.getElementById("root"));
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/8.4.1/reactstrap.min.js"></script>
<div id="root"></div>