Sivadass N Sivadass N - 3 months ago 13
React JSX Question

React onClick state is not toggling back

I am building a menubar using React JS. On clicking a menu item, respective megamenu should open. On clicking outside of the megamenu, it should close itself. I have made upto this by toggling the state of the megamenu. But I also want to close the megamenu, when the menu-item is clicked second-time(i.e. toggle close and open of megamenu by clicking menu-item). I am stuck here, on clicking the menu-item second time, the state is not toggling back.

class Menubar extends React.Component {
constructor() {
super();
this.state = {
clicked: false
};
this.handleClick = this.handleClick.bind(this);
this.handleOutsideClick = this.handleOutsideClick.bind(this);
}

componentWillMount() {
document.addEventListener('click', this.handleOutsideClick, false);
}

componentWillUnmount(){
document.removeEventListener('click', this.handleOutsideClick, false);
}

handleClick() {
this.setState({clicked: !this.state.clicked});
}

handleOutsideClick(){
if (this.refs.megaMenu.contains(event.target)) {
} else {
this.setState({
clicked: false
});
}
}

render() {
return (
<div className="container">
<div className="menu-bar">
{/* Menu*/}
<div className="menu-bar-item">
<a className="menu-bar-link" href="#" onClick={this.handleClick}>Points</a>
<div className={"mega-menu"+" "+this.state.clicked} ref="megaMenu">
<div className="mega-menu-content">
<p>Points Menu</p>
</div>
</div>
</div>
</div>
</div>
);
}
}

ReactDOM.render(
<Menubar />,
document.getElementById('example')
);


Codepen Demo

Answer

You need to move ref="megaMenu" so it includes button as well, otherwise when you click on button handleOutsideClick is also triggered and you flip this.state.clicked twice. Also, you forgot to pass event in handleOutsideClick handler.

class Menubar extends React.Component {
  constructor() {
    super();
    this.state = {
      clicked: false
    };
    this.handleClick = this.handleClick.bind(this);
    this.handleOutsideClick = this.handleOutsideClick.bind(this);
  }

  componentWillMount() {
    document.addEventListener('click', this.handleOutsideClick, false);
  }

  componentWillUnmount() {
    document.removeEventListener('click', this.handleOutsideClick, false);
  }

  handleClick() {
    this.setState({clicked: !this.state.clicked});
  }

  handleOutsideClick(event) {
    if (!this.menu.contains(event.target)) {
      this.setState({
        clicked: false
      });
    }
  }

  render() {
    return (
      <div className="container">
        <div className="menu-bar">

          {/* Menu*/}
          <div className="menu-bar-item" ref={el => this.menu = el}>
            <a className="menu-bar-link" href="#" onClick={this.handleClick}>Points</a>
            <div className={"mega-menu" + " " + this.state.clicked}>
              <div className="mega-menu-content">
                <p>Points Menu</p>
              </div>
            </div>
          </div>

        </div>
      </div>
    );
  }
}

ReactDOM.render(
  <Menubar />,
  document.getElementById('example')
);

I've fixed your Codepen Demo

Also, consider using callback refs like ref={el => this.menu = el} It's a better way to do it.

Comments