简体   繁体   中英

How have toggle dropdown on button click and close on outside click?

I have Dropdown which is open by clicking on button and closed on outside click.

This is function which toggle dropdown:

toggleAllCategories = () => {
  this.setState({ isOpenAllCategories: !this.state.isOpenAllCategories });
};

That mean by clicking on button you should open and close Dropdown.

But in same time I have implemented with react-refs that click outside of body of dropdown ---> close dropdown.

That make bug - reproduction:

Step 1 : Click on "All Categories" btn

Result: Dropdown is open

Step 2 : Click again on "All Categories" btn - cus want to close dropdown

Result: As result Dropdown is open.

  1. Clicked on "All categories" btn (state is changed isOpenAllCategories = true)

HERE is the problem --> 2. Click Again on "All Categories" btn

  • First is called handleOutsideClick() function which sets isOpenAllCategories on false .

  • Then is called toggleAllCategories() which change state on oposite of current value isOpenAllCategories: !this.state.isOpenAllCategories and that is true cus handleOutsideClick() already change state on false .

How have toggle dropdown on button click and close on outside click?

All Categories Dropdown component:

class AllCategories extends Component {

  componentDidMount() {
    document.addEventListener('mousedown', (e) => this.handleClickOutside(e));
  }

  componentWillUnmount() {
    document.removeEventListener('mousedown', (e) => this.handleClickOutside(e));
  }

  setWrapperRef(node) {
    this.wrapperRef = node;
  }

  handleClickOutside = (event) => {
    if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
      this.props.closeAllCategories();
    }
  };

  render() {
    return (
      <div className="all-categories-wrapper">
        <div className="all-categories" ref={(node) => this.setWrapperRef(node)}>
          <ul className="all-categories-list">
            <li className="all-categories-list-item">All Categories</li>
            {this.state.allCategories.map((category) => (
              <li
                className={`all-categories-list-item ${
                  category.selected ? 'all-categories-list-item-active' : ''
                }`}
              >
                {category.name}
              </li>
            ))}
          </ul>
        </div>
      </div>
    );
  }
}

All Categories Button component:

export default ({ toggleAllCategories, className }) => (
  <div className="category" onClick={() => toggleAllCategories()} role="button">
    <div className={`category-button-wrapper ${className}`}>
      <button className="category-button">
        Sports <span className="category-button-slash">/</span> Football
      </button>
      <div className="category-icon-box">
        <span className="category-icon">
          <i className="material-icons md-30 md-white">expand_more</i>
        </span>
      </div>
    </div>
  </div>
);

Code isn't working because of the scope of this in your component functions. You'll have to bind your functions in the constructor of your component. Or change your functions to ES6 which will solve the issue

//binding
constructor(props){
  super(props)
this.handleClickOutside = this.handleClickOutside.bind(this);
}
handleClickOutside = () => {
 //code here
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM