Reactjs Dropdown Menu, Close Other Menus Before Opening Current
Solution 1:
since react got one way data flow you need to do this in parent component
import React from 'react'classDropdownParentextendsReact.Component{
state = {
openedDropdown: null,
};
render() {
return (
<React.Fragment>
<Dropdown
name="dropdown1"
isOpen={this.state.openedDropdown === 1}
onClick={() => this.setState({ openedDropdown: 1 })}
onClose={() => this.setState({ openedDropdown: null })}
/>
<Dropdown
name="dropdown2"
isOpen={this.state.openedDropdown === 2}
onClick={() => this.setState({ openedDropdown: 2 })}
onClose={() => this.setState({ openedDropdown: null })}
/>
</React.Fragment>
)
}
}
and then you need Dropdown to be based on props.isOpen, not on your state.menuOpen and use props.onClick / props.onClose instead of this.showDropdown
Alternatively you can base on mouse (like onMouseDownonMouseEnter...) or focus (onFocusonBlur) events but this is hard to get mobile friendly and won't ensure you about "only one dropdown at the same moment". more info: https://reactjs.org/docs/events.html
Solution 2:
Since being open is not an information encapsulated in your dropdown's state anymore, I would suggest moving up this information up to your parent component's state.
You should now convert your Dropdowns into stateless functions :
const DropdownMenu = ({ menuOpen, count, text, showDropdown }) => ( //props deconstruction
<div className="dropdown__menu" onClick={showDropdown}>
{text}
{count && <b>{count}</b>} //If you do not want to show anything if a condition is falsy, use the inline if &&
<div className="dropdown__content" style={{ 'display': menuOpen ? 'block' : 'none' }}> //You can put the ternary condition directly into the JSON
{this.props.children}
</div>
</div>
)
You will now have to store which dopdown is opened in your parent component :
classParentextendsComponent {
state = {
openedDropdown = null;
}
And send a callback function to your dropdown :
dropdownClicked = openedDropdown =>ev => {
this.setState({ openedDropdown })
}
<DropdownMenu text="New" count={127} disabled showDropdown={this.dropdownClicked('New')} menuOpen={this.state.openedDropdown === 'New'}/>
<DropdownMenutext="Only show"showDropdown={this.dropdownClicked('Only')} menuOpen={this.state.openedDropdown === 'Only'}><li>New</li><li>Old</li></DropdownMenu><DropdownMenutext="Other"showDropdown={this.dropdownClicked('Other')} menuOpen={this.state.openedDropdown === 'Other'}><li>one</li><li>two</li></DropdownMenu><DropdownMenutext="Sort by"showDropdown={this.dropdownClicked('Sort')} menuOpen={this.state.openedDropdown === 'Sort'}><li>Name</li><li>Age</li><li>Value</li></DropdownMenu>
Solution 3:
You can play with onBlur
since it loses focus when another is being cliked.
classDropDownextendsReact.Component {
state = {
isVisible: false
}
closeMenu = () => {
this.setState({ isVisible: false })
}
toggleMenu = () => {
this.setState(prevState => ({ isVisible: !prevState.isVisible }))
}
render() {
const { isVisible } = this.state;
return (
<divclassName="dropdown__menu"onBlur={this.closeMenu}tabIndex={0}role="menu"onClick={this.toggleMenu}>
{isVisible ? 'visible' : 'hidden'}
</div>
)
}
}
constApp = () => (
<div><DropDown /><DropDown /></div>
)
ReactDOM.render(<App />, document.getElementById('root'));
.dropdown__menu {
outline: none;
}
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script><divid="root"></div>
Solution 4:
I would use redux for that. https://redux.js.org/
This solution allows you to manage state for whole app. It could be used for storing info about opened dropdown, and change it - if necessary.
Post a Comment for "Reactjs Dropdown Menu, Close Other Menus Before Opening Current"