Most of the time, your components respond to events that occur within the component tree by defining their own handler or by accepting a handler defined by a parent component via props. Sometimes, this isn't enough. In this lesson, we'll rely on lifecycle hooks and good old fashioned DOM events to update state in a React component in response to an event that occurs outside of the component tree.
class Menu extends React.Component { constructor(props) { super(props) this.state = { isVisible: false } this.handleClickOutside = this.handleClickOutside.bind(this) this.toggleOptions = this.toggleOptions.bind(this) this.handleClick = this.handleClick.bind(this) } componentDidMount() { document.addEventListener('click', this.handleClickOutside) } componentWillUnmount() { document.removeEventListener('click', this.handleClickOutside) } handleClickOutside(evt) { if (!this.node.contains(evt.target)) { this.setState({ isVisible: false }) } } toggleOptions(evt) { evt.preventDefault() this.setState(state => ({ isVisible: !state.isVisible })) } handleClick(choice, evt) { evt.preventDefault() this.props.handleMenuChoice(choice) this.setState({ isVisible: false }) } render() { return ( <div className="menu" ref={el => (this.node = el)}> <a href="#" onClick={this.toggleOptions}> Options </a> {this.state.isVisible ? <div className="menuContents"> <a href="#" onClick={this.handleClick.bind(null, 'one')}> One </a> <a href="#" onClick={this.handleClick.bind(null, 'two')}> Two </a> <a href="#" onClick={this.handleClick.bind(null, 'three')}> Three </a> <a href="#" onClick={this.handleClick.bind(null, '')}> Clear Selection </a> </div> : null} </div> ) } }
The most important thing is how to detect whether user click outside the menu or not.
To do that, we use 'ref':
<div className="menu" ref={el => (this.node = el)}>
We assign the elememt to vairable 'this.node'.
console log the node:
<div class="menu"> <a href="#">options</a> </div>
When we click inside the menu, the 'evt.target' is the 'a' tag.
If we click outside the menu, then then 'evt.target' is the whole html tag.
Therefore, we can check:
if (!this.node.contains(evt.target)) {