Building Dynamic Menu With Click Handler
Solution 1:
Update: Added sample menu implementation
I realize that stylistically this isn't what you're going for, but conceptually this is the direction I think you'd want to go:
const languages = [
"English",
"Spanish",
"French",
"Wookie",
"Klingon"
]
functionMenu () {
// keep track of whether the menu is openconst [isOpen, setOpen] = React.useState(false);
// in a real app this would notify interested// parties (redux dispatch or whatever)const [selectedLanguage, setLanguage] = React.useState(languages[0]);
// convenience function for setting the language// and closing the menuconstonLangSelect = language => {
setLanguage(language);
setOpen(false);
};
return (
<div><buttononClick={() => setOpen(!isOpen)}>{selectedLanguage} v</button><ulclassName="language-menu">
{
// if the menu is open, render the items…
isOpen && (
// by iterating over the available languages and emitting an item for each.
// item.onClick invokes onLangSelect, passing in the selected language
// we're also flagging the current item with a css class here
languages.map(lang => (
<liclassName={selectedLanguage === lang ? 'selected' : ''} onClick={() => onLangSelect(lang)} key={lang}>
{lang}
</li>
))
)
}
</ul></div>
)
}
ReactDOM.render(<Menu />, document.querySelector("#app"))
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
.language-menu {
position: absolute;
background: white;
font-size: 0.875rem;
min-width: 150px;
padding: 0;
margin: 0;
list-style: none;
}
.language-menuli {
padding: 1em;
}
.language-menuli:hover {
background: lightblue;
}
li.selected {
background: bisque;
}
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.0/umd/react.production.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.0/umd/react-dom.production.min.js"></script><divid="app"></div>
You shouldn't be manipulating the DOM directly. Doing so undercuts the entire point of React. Just emit the nodes you need. So instead of this:
// don't do this. there's no need to manually create dom elements in react
[...props.ui.languages].forEach(function (language, i) {
const el = document.createElement('li')
el.value = language.languageCode
el.innerHTML = language.name
el.className = "item"
el.addEventListener("click", function(e) {
e.stopPropagation()
})
listContainer.appendChild(el)
})
Just emit the markup with jsx:
// within render method
[...props.ui.languages].map((language) => (
<likey={language}onclick={...}>{language}</li>
))
When you do this:
// this sets onClick to undefined because openMenu doesn't return anything
onClick={openMenu(this)}
You're setting the onclick
handler to the return value of openMenu(this)
, which is undefined
since openMenu
doesn't return anything.
And again, don't manipulate the DOM. Instead of:
e.parentElement.classList.add('open')
Use setState
to track whether the menu is open or not:
this.setState({open: true});
And then render accordingly:
const {open} = this.state;
<div className={open ? 'menu open' : 'menu'}>
...
</div>
(There are packages like clsx that can assist with composing classnames; I'm emitting menu open
manually here to keep the example simple.)
Post a Comment for "Building Dynamic Menu With Click Handler"