我正在寻找一种方法来检测单击事件是否发生在组件之外,如本文所述。jQueryclosest()用于查看单击事件的目标是否将dom元素作为其父元素之一。如果存在匹配项,则单击事件属于其中一个子项,因此不被视为在组件之外。

因此,在我的组件中,我想将一个单击处理程序附加到窗口。当处理程序启动时,我需要将目标与组件的dom子级进行比较。

click事件包含类似“path”的财产,它似乎保存了事件经过的dom路径。我不知道该比较什么,或者如何最好地遍历它,我想肯定有人已经把它放在了一个聪明的效用函数中。。。不


当前回答

战略示例

我喜欢所提供的解决方案,这些解决方案通过围绕组件创建包装器来完成相同的任务。

由于这更多是一种行为,我想到了战略,并提出了以下建议。

我是React的新手,我需要一些帮助来保存用例中的样板

请回顾并告诉我你的想法。

ClickOutside行为

import ReactDOM from 'react-dom';

export default class ClickOutsideBehavior {

  constructor({component, appContainer, onClickOutside}) {

    // Can I extend the passed component's lifecycle events from here?
    this.component = component;
    this.appContainer = appContainer;
    this.onClickOutside = onClickOutside;
  }

  enable() {

    this.appContainer.addEventListener('click', this.handleDocumentClick);
  }

  disable() {

    this.appContainer.removeEventListener('click', this.handleDocumentClick);
  }

  handleDocumentClick = (event) => {

    const area = ReactDOM.findDOMNode(this.component);

    if (!area.contains(event.target)) {
        this.onClickOutside(event)
    }
  }
}

示例用法

import React, {Component} from 'react';
import {APP_CONTAINER} from '../const';
import ClickOutsideBehavior from '../ClickOutsideBehavior';

export default class AddCardControl extends Component {

  constructor() {
    super();

    this.state = {
      toggledOn: false,
      text: ''
    };

    this.clickOutsideStrategy = new ClickOutsideBehavior({
      component: this,
      appContainer: APP_CONTAINER,
      onClickOutside: () => this.toggleState(false)
    });
  }

  componentDidMount () {

    this.setState({toggledOn: !!this.props.toggledOn});
    this.clickOutsideStrategy.enable();
  }

  componentWillUnmount () {
    this.clickOutsideStrategy.disable();
  }

  toggleState(isOn) {

    this.setState({toggledOn: isOn});
  }

  render() {...}
}

笔记

我想到了存储传递的组件生命周期挂钩,并用类似的方法覆盖它们:

const baseDidMount = component.componentDidMount;

component.componentDidMount = () => {
  this.enable();
  baseDidMount.call(component)
}

component是传递给ClickOutsideBehavior构造函数的组件。这将从该行为的用户中删除启用/禁用样板,但看起来不太好

其他回答

这是我的方法(演示-https://jsfiddle.net/agymay93/4/):

我创建了一个名为WatchClickOutside的特殊组件,它可以像这样使用(我假设JSX语法):

<WatchClickOutside onClickOutside={this.handleClose}>
  <SomeDropdownEtc>
</WatchClickOutside>

以下是WatchClickOutside组件的代码:

import React, { Component } from 'react';

export default class WatchClickOutside extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  componentWillMount() {
    document.body.addEventListener('click', this.handleClick);
  }

  componentWillUnmount() {
    // remember to remove all events to avoid memory leaks
    document.body.removeEventListener('click', this.handleClick);
  }

  handleClick(event) {
    const {container} = this.refs; // get container that we'll wait to be clicked outside
    const {onClickOutside} = this.props; // get click outside callback
    const {target} = event; // get direct click event target

    // if there is no proper callback - no point of checking
    if (typeof onClickOutside !== 'function') {
      return;
    }

    // if target is container - container was not clicked outside
    // if container contains clicked target - click was not outside of it
    if (target !== container && !container.contains(target)) {
      onClickOutside(event); // clicked outside - fire callback
    }
  }

  render() {
    return (
      <div ref="container">
        {this.props.children}
      </div>
    );
  }
}

你可以用一个简单的方法来解决你的问题,我向你展示:

....

const [dropDwonStatus , setDropDownStatus] = useState(false)

const openCloseDropDown = () =>{
 setDropDownStatus(prev => !prev)
}

const closeDropDown = ()=> {
 if(dropDwonStatus){
   setDropDownStatus(false)
 }
}
.
.
.
<parent onClick={closeDropDown}>
 <child onClick={openCloseDropDown} />
</parent>

这对我有用,祝你好运;)

带打字稿函数Tooltip():ReactElement{const[show,setShow]=useState(false);const-ref=useRef<HTMLDivElement>(null);使用效果(()=>{函数handleClickOutside(事件:MouseEvent):void{if(ref.current&&!ref.current.includes(event.target作为节点)){setShow(false);}}//绑定事件侦听器document.addEventListener('mouseown',handleClickOutside);返回()=>{//清理时取消绑定事件侦听器document.removeEventListener('usedown',handleClickOutside);};});返回(<div ref={ref}></div>) }

import React, { useState, useEffect, useRef } from "react";

const YourComponent: React.FC<ComponentProps> = (props) => {
  const ref = useRef<HTMLDivElement | null>(null);
  const [myState, setMyState] = useState(false);
  useEffect(() => {
    const listener = (event: MouseEvent) => {
      // we have to add some logic to decide whether or not a click event is inside of this editor
      // if user clicks on inside the div we dont want to setState
      // we add ref to div to figure out whether or not a user is clicking inside this div to determine whether or not event.target is inside the div
      if (
        ref.current &&
        event.target &&
        // contains is expect other: Node | null
        ref.current.contains(event.target as Node)
      ) {
        return;
      }
      // if we are outside
      setMyState(false);
    };
    // anytime user clics anywhere on the dom, that click event will bubble up into our body element
    // without { capture: true } it might not work
    document.addEventListener("click", listener, { capture: true });
    return () => {
      document.removeEventListener("click", listener, { capture: true });
    };
  }, []);

  return (
    <div  ref={ref}>
      ....
    </div>
  );
};

我从下面的文章中发现了这一点:

render(){返回({this.node=节点;}}>切换弹出窗口{this.state.popupVisible&&(我是个酒鬼!)});}}

这里有一篇关于这个问题的精彩文章:“处理React组件外部的点击”https://larsgraubner.com/handle-outside-clicks-react/