Skip to content Skip to sidebar Skip to footer

Reactjs Dropdown Menu, Close Other Menus Before Opening Current

I have made some dropdown menus: export default class DropdownMenu extends Component { constructor(props) { super(props) this.state = { menuOpen:

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"