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'
class DropdownParent extends React.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 onMouseDown onMouseEnter...) or focus (onFocus onBlur) 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 :
class Parent extends Component {
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'}/>
<DropdownMenu text="Only show" showDropdown={this.dropdownClicked('Only')} menuOpen={this.state.openedDropdown === 'Only'}>
<li>New</li>
<li>Old</li>
</DropdownMenu>
<DropdownMenu text="Other" showDropdown={this.dropdownClicked('Other')} menuOpen={this.state.openedDropdown === 'Other'}>
<li>one</li>
<li>two</li>
</DropdownMenu>
<DropdownMenu text="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.
class DropDown extends React.Component {
state = {
isVisible: false
}
closeMenu = () => {
this.setState({ isVisible: false })
}
toggleMenu = () => {
this.setState(prevState => ({ isVisible: !prevState.isVisible }))
}
render() {
const { isVisible } = this.state;
return (
<div
className="dropdown__menu"
onBlur={this.closeMenu}
tabIndex={0}
role="menu"
onClick={this.toggleMenu}>
{isVisible ? 'visible' : 'hidden'}
</div>
)
}
}
const App = () => (
<div>
<DropDown />
<DropDown />
</div>
)
ReactDOM.render(<App />, document.getElementById('root'));
.dropdown__menu {
outline: none;
}
<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>
<div id="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"