我有两个组件,容器和物品.

  • 容器包含项目.
  • 项包含一个按钮和一个div.

这些组件具有以下行为:

  • Container:当我点击容器的outside时,它应该会消失,我通过使用一个自定义钩子来实现这一点,该钩子检测组件外部的点击.这很好用.
  • Item:当我点击div时,它应该消失,这是inside项,我通过设置一个布尔状态来实现.除problem here is that Container also disappears外,这也有效.

Container

const Container = ({ setDisplay }) => {
  const containerRef = useRef(null);
  useClickOutside(containerRef, () => {
    //code to make Container disappear that is not relevant for the issue
    setDisplay(false)
  });
  return (
    <div ref={containerRef} className='container'>
      <Item />
    </div>
  );
};

Item

const Item = () => {
  const [displayItem, setDisplayItem] = useState(false);
  return (
    <div>
      <button onClick={() => setDisplayItem(true)}>Show Item's content</button>
      {displayItem && (
        <div
          className='item-content'
          onClick={() => setDisplayItem(false)}
        />
      )}
    </div>
  );
};

useClickOutside

const useClickOutside = (ref, handler) => {
  useEffect(() => {
    const trigger = e => {
      if (!(ref?.current?.contains(e.target))) handler();
    }
    document.addEventListener('click', trigger);
    return () => document.removeEventListener('click', trigger);
  }, [handler, ref])
}

为什么会发生这种情况,我该如何预防?

注意:我必须用那个钩子.

推荐答案

两个侦听器都连接到冒泡阶段,因此内部侦听器首先触发.

当项目显示并单击时,将运行:

<div
  className='item-content'
  onClick={() => setDisplayItem(false)}
>item content</div>

因此,在事件向外传播之前,setDisplayItem(false)会导致这个.item-content元素从DOM中删除.看这里,父母如何在之后不再存在:

const Container = ({ setDisplay }) => {
  const containerRef = React.useRef(null);
  useClickOutside(containerRef, () => {
    //code to make Container disappear that is not relevant for the issue
    console.log('making container disappear');
  });
  return (
    <div ref={containerRef} className='container'>
      container
      <Item />
    </div>
  );
};
const Item = () => {
  const [displayItem, setDisplayItem] = React.useState(false);
  return (
    <div>
      <button onClick={() => setDisplayItem(true)}>Show Item's content</button>
      {displayItem && (
        <div
          className='item-content'
          onClick={() => setDisplayItem(false)}
        >item content</div>
      )}
    </div>
  );
};
const useClickOutside = (ref, handler) => {
  React.useEffect(() => {
    const trigger = e => {
      console.log(e.target.parentElement);
      if (!(ref.current.contains(e.target))) handler();
    }
    document.addEventListener('click', trigger);
    return () => document.removeEventListener('click', trigger);
  }, [handler, ref])
}


ReactDOM.render(<Container />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>

您可以通过更改useClickOutside来修复此问题,同时判断 node 是否已连接.如果未连接,则由于状态更改和重新渲染,元素不再位于DOM中-因此所做的单击不是ref.current之外的definitely,因此处理程序不应运行.

const trigger = e => {
  const { current } = ref;
  if (e.target.isConnected && !current.contains(e.target)) {

const Container = ({ setDisplay }) => {
  const containerRef = React.useRef(null);
  useClickOutside(containerRef, () => {
    //code to make Container disappear that is not relevant for the issue
    console.log('making container disappear');
  });
  return (
    <div ref={containerRef} className='container'>
      container
      <Item />
    </div>
  );
};
const Item = () => {
  const [displayItem, setDisplayItem] = React.useState(false);
  return (
    <div>
      <button onClick={() => setDisplayItem(true)}>Show Item's content</button>
      {displayItem && (
        <div
          className='item-content'
          onClick={() => setDisplayItem(false)}
        >item content</div>
      )}
    </div>
  );
};
const useClickOutside = (ref, handler) => {
  React.useEffect(() => {
    const trigger = e => {
      const { current } = ref;
      if (e.target.isConnected && !current.contains(e.target)) {
        console.log(current.parentElement);
        handler();
      }
    }
    document.addEventListener('click', trigger);
    return () => document.removeEventListener('click', trigger);
  }, [handler, ref])
}


ReactDOM.render(<Container />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>

Javascript相关问答推荐

将下拉分区与工具提示结合起来

Mongodb拥有5亿个文档,我想根据JavaScript驱动程序中的两个字段使用regEx进行搜索,而不是模式

我无法在NightWatch.js测试中获取完整的Chrome浏览器控制台日志(log)

没有输出到带有chrome.Devtools扩展的控制台

模块与独立组件

在页面上滚动 timeshift 动垂直滚动条

Google Apps脚本中的discord邀请API响应的日期解析问题

阿波罗返回的数据错误,但在网络判断器中是正确的

变量的值在Reaction组件中的Try-Catch语句之外丢失

如何使用TypeScrip设置唯一属性?

如何在.NET Core中将chtml文件链接到Java脚本文件?

Reaction组件在本应被设置隐藏时仍显示

TypeError:无法读取未定义的属性(正在读取';宽度';)

使用插件构建包含chart.js提供程序的Angular 库?

未捕获的运行时错误:调度程序为空

更新Redux存储中的对象数组

使用RxJS from Event和@ViewChild vs KeyUp事件和RxJS主题更改输入字段值

如何在脚本编译后直接将RxJ模块导入浏览器(无需Angel、webpack、LiteServer)

固定动态、self 调整的优先级队列

在将元素追加到DOM之前,createElement()是否会触发回流?混淆abt DocumentFragment行为