React handling在外面点击多个元素

时间:2017-11-02 15:37:25

标签: javascript node.js reactjs

我正在尝试模仿标题,如果单击下拉菜单,它将显示弹出菜单,单击弹出菜单元素外部,它将在标题中的多个元素中消失。

这是我修改过的香草笔,却无法让它工作: https://codepen.io/anon/pen/JOGGzL

handleClick() {
    if (!this.state.popupVisible) {
      document.addEventListener('click', this.handleOutsideClick, false);
    } else {
      document.removeEventListener('click', this.handleOutsideClick, false);
    }

    this.setState(prevState => ({
       popupVisible: !prevState.popupVisible,
    }));
}

handleOutsideClick(e) {
    if (this.node.contains(e.target)) {
      return;
    }

    this.handleClick();
}

我尝试创建唯一的引用并通过handleClick和handleOutsideClick传递参数以区分两个不同的弹出按钮,但我遇到了一个问题,它似乎产生了很多EventListeners而没有正确删除它们

如果用户点击外部弹出元素,一次切换一个按钮并停用所有按钮的最优雅方法是什么?我是否必须创建单独的组件来处理这个问题?

由于

4 个答案:

答案 0 :(得分:1)

而不是在实际处理程序中添加和删除事件,您可以使用react的生命周期方法:

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

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

你的handleOnClick方法可以执行标准的切换状态,但是如果不是下拉列表或包含目标,handleOutsideClick可以总是将其设置为false:

handleOutsideClick(event) {
    if (this.node === event.target || !this.node.contains(event.target)) {
        this.setState(prevState => ({
             popupVisible: false
        }));
    }
}

处理多个弹出窗口的最优雅的方法是让一个类执行上面所见的所有逻辑,称为Popover,它呈现子组件。您可以在父组件中重复使用它,如下所示:

const MorePops = () => (
    <div>
        <Popover label={'label text'}>
            <div>{'Child'}</div>
        </Popover>
        <Popover label={'label text'}>
            <div>{'Child'}</div>
        </Popover>
        <Popover label={'label text'}>
            <div>{'Child'}</div>
        </Popover>
        <Popover label={'label text'}>
            <div>{'Child'}</div>
        </Popover>
    </div>
);

答案 1 :(得分:1)

据我了解,您希望弹出窗口可以相互独立切换。最快的方法是将popover移动到单独的组件。

class Popover extends React.Component {
    constructor() {
    super();

    this.handleClick = this.handleClick.bind(this);
    this.handleOutsideClick = this.handleOutsideClick.bind(this);

    this.state = {
      popupVisible: false
    };
  }

  handleClick() {
    if (!this.state.popupVisible) {
      // attach/remove event handler
      document.addEventListener('click', this.handleOutsideClick, false);
    } else {
      document.removeEventListener('click', this.handleOutsideClick, false);
    }

    this.setState(prevState => ({
       popupVisible: !prevState.popupVisible,
    }));
  }

  handleOutsideClick(e) {
    // ignore clicks on the component itself
    if (this.node.contains(e.target)) {
      return;
    }

    this.handleClick();
  }

  render() {
    return (
      <div className="popover-container" ref={node => {this.node=node; }}>
        <button onClick={this.handleClick}>
          Toggle Popover
        </button>
        { this.state.popupVisible && <div className="popover">I'm a popover!</div> }
      </div>
    )
  }
}

这是你的根组件

class App extends React.Component {
  render() {
    return (
      <div>
        <Popover/>
        <Popover/>
      </div>
    );
   }
}

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

答案 2 :(得分:0)

我会添加删除componentdidmount生命周期方法中的所有弹出窗口并删除handleoutsideclick方法:

componentDidMount() {
document.addEventListener('click', (e) => {
  console.log(e.target);
  if ([].slice.call(document.querySelectorAll('.popover-container button')).indexOf(e.target) === -1) {
        this.setState({
          popupVisible: false,
         });
       return;
     }
   }, true);
 }

检查此笔:https://codepen.io/Marouen/pen/vWLKKK

答案 3 :(得分:0)

如果要使用此功能已经存在的很小的组件(466字节压缩),则可以签出该库react-outclick。它使您可以检测组件/元素外部的事件。

关于库的好处是,它还使您可以检测组件外部和组件内部的单击(嵌套弹出窗口和模式需要)。它还支持检测其他类型的事件。