在 React 中添加带有超时的悬停效果

Posted

技术标签:

【中文标题】在 React 中添加带有超时的悬停效果【英文标题】:Adding hovering effects with a timeout in React 【发布时间】:2020-10-28 06:18:34 【问题描述】:

See codesandbox here

我正在尝试添加一个在将鼠标悬停在 div 上时会延迟显示的模式。但是,它变得有点棘手,因为例如,如果超时间隔为 1000 毫秒,并且您将鼠标悬停在所述 div 上,然后在 1000 毫秒内悬停在远离该 div 的位置,模态仍然会显示。我想要发生的是,模式在延迟(例如 1000 毫秒)后显示,如果您在该延迟期间将鼠标悬停在 div 上。如何创建这种效果而不是我现在看到的副作用?谢谢!

index.tsx:

import * as React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

const Modal: React.FC = () => 
  const divRef = React.useRef<htmlDivElement>(null);
  const [showModal, setShowModal] = React.useState<boolean>(false);

  React.useEffect(() => 
    const divNode = divRef.current;

    const handleEvent = (event: Event): void => 
      if (divNode) 
        if (divNode.contains(event.target as Node)) 
          setTimeout(() => setShowModal(true), 1000);
         else 
          setShowModal(false);
        
      
    ;

    document.addEventListener("mouseover", handleEvent);

    return () => 
      document.removeEventListener("mouseover", handleEvent);
    ;
  , [divRef]);

  return (
    <div className="container">
      <div className="div" ref=divRef>
        Hover Me
      </div>
      showModal && <div className="modal">modal</div>
    </div>
  );
;

const App: React.FC = () => (
  <>
    <Modal />
    <Modal />
    <Modal />
    <Modal />
  </>
);

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

【问题讨论】:

【参考方案1】:

您应该添加一个将隐藏模式的鼠标移出事件。

在 'mouseout' 事件监听器上调用函数并将 showModal 设置为 false。这样,如果您随时移动鼠标,它将隐藏模式。

setShowModal(false)

更新:您是否也可以将超时设置为变量,然后在鼠标移出时触发 clearTimeout(variable_that_set_to_timeout)

 React.useEffect(() => 
    const divNode = divRef.current;
    let timeout = null;

    const handleEvent = (event: Event): void => 
      if (divNode) 
        if (divNode.contains(event.target as Node)) 
          timeout = setTimeout(() => setShowModal(true), 1000);
         else 
          setShowModal(false);
        
      
    ;

    const hideModal = (event: Event): void => 
      clearTimeout(timeout);
      setShowModal(false);
    ;

    divNode.addEventListener("mouseover", handleEvent);

    divNode.addEventListener("mouseout", hideModal);

    return () => 
      document.removeEventListener("mouseover", handleEvent);
    ;
  , [divRef]);

Link of sandbox

【讨论】:

感谢@Niloy,但这不会阻止超时函数在延迟期后被调用。【参考方案2】:

在使用 react 时,您确实应该避免更改 DOM。 React 不是 jQuery。 您可以尝试将此作为模式代码:

const Modal: React.FC = () => 
  const [timeout, setModalTimeout] = React.useState(null);
  const [showModal, setShowModal] = React.useState<boolean>(false);
  return (
    <div className="container">
      <div className="div" onMouseEnter=() => 
         timeout && !showModal && clearTimeout(timeout);
         setModalTimeout(setTimeout(() => setShowModal(true), 1000))
       onMouseLeave=() => 
        timeout && clearTimeout(timeout)
        setShowModal(false);
      >
        Hover Me
      </div>
      showModal && <div className="modal">modal</div>
    </div>
  );
;

来源:

https://reactjs.org/docs/refs-and-the-dom.html https://reactjs.org/docs/react-dom.html

【讨论】:

如果你想让de modal在用户悬停时不关闭,基本上停止作为dropdown工作,你可以添加!showModal &amp;&amp; setShowModal(false)代替setShowModalFalse() 谢谢亚瑟,但我需要使用事件监听器。此示例是我在应用程序中构建的特定实现的抽象,我还将引用附加到 children 并基于多个节点事件执行这些事件。 代码应该可以工作。只需将您的代码从“mouseover”更改为 mouseenter 并添加一个 mouseleave。但是我们很清楚,你不需要使用 dom,即使有多个组件。如果您想在各种组件上触发某些内容,只需添加上下文或其他内容,然后添加用于更改每个单独组件上的所述上下文的代码。 我的代码的重要部分不是我放置事件的位置,而是超时。【参考方案3】:

执行此操作的正确方法是创建一个useTimeout 挂钩并管理维护悬停状态。

import  useState  from "react";
import useTimeout from "./useTimeout";

export default function App() 
  const [visible, setVisible] = useState(false);
  const [hovered, setHovered] = useState(false);

  //close after 3s
  useTimeout(() => setVisible(true), !visible && hovered ? 3000 : null);

  return (
    <div className="App">
      <h1>Hover Timeout Example</h1>
      <div
        onMouseEnter=() => setHovered(true)
        onMouseLeave=() => setHovered(false)
      >
        Hover me for 3s to show modal
        <div>Hover status: hovered ? "true" : "false"</div>
      </div>
      
      visible && (
        <div>
          <h1>Modal</h1>
          <div>
            <button onClick=() => setVisible(false)>close</button>
          </div>
        </div>
      )
    </div>
  );


Code Sandbox

【讨论】:

以上是关于在 React 中添加带有超时的悬停效果的主要内容,如果未能解决你的问题,请参考以下文章

带有 React-Native 的 android 模拟器上的位置请求超时

在 Reactjs 的页面中添加会话超时

经常在带有 shadow-cljs 和 React Native 的 Cider 中看到“REPL 命令超时”。必须重启模拟器才能修复

React Bootstrap,为 NavItems 添加悬停效果

超时 jQuery 效果

添加带有图像的悬停效果(wordpress)