【问题标题】:Adding hovering effects with a timeout in React在 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);

【问题讨论】:

    标签: javascript html css reactjs typescript


    【解决方案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>
      );
    };
    

    来源:

    【讨论】:

    • 如果你想让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

    【讨论】:

      猜你喜欢
      • 2012-01-21
      • 2018-03-29
      • 1970-01-01
      • 1970-01-01
      • 2014-11-08
      • 2018-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多