【问题标题】:Delay React onMouseOver event延迟反应 onMouseOver 事件
【发布时间】:2018-05-22 11:37:38
【问题描述】:

我有一个元素列表,当悬停其中一个时,我想改变我的状态。

<ListElement onMouseOver={() => this.setState({data})}>Data</ListElement>

不幸的是,如果我将鼠标移到列表上,我的状态会快速连续更改几次。我想延迟状态更改,以便它在被解雇之前等待半秒。有办法吗?

【问题讨论】:

  • 您确定要延迟吗?您不能删除事件侦听器,然后在鼠标离开时再次添加它吗?或者在设置状态之前运行检查?
  • 为什么不使用onMouseEnteronMouseLeave

标签: javascript reactjs events mouseevent dom-events


【解决方案1】:

这是一种可以使用onMouseEnteronMouseLeavesetTimeout 的组合将事件延迟500 毫秒的方法。

请记住,您的数据的状态更新可以由父组件管理并作为道具传入。

import React, { useState } from 'react'

const ListElement = () => {
    const [data, setData] = useState(null)
    const [delayHandler, setDelayHandler] = useState(null)

    const handleMouseEnter = event => {
        setDelayHandler(setTimeout(() => {
            const yourData = // whatever your data is

            setData(yourData)
        }, 500))
    }

    const handleMouseLeave = () => {
        clearTimeout(delayHandler)
    }

    return (
        <div
            onMouseEnter={handleMouseEnter}
            onMouseLeave={handleMouseLeave}
        >
            I have a delayed event handler
        </div>
    )
}

export default ListElement

【讨论】:

  • 给定状态没有同步变化,如果用户进入和离开的速度很快,是否有可能没有设置delayHandler值?
  • 最好的答案。这应该是正确的答案。
  • 非常有帮助。最小和最干净的解决方案(尽管仍然需要存储两个状态......处理超时总是很烦人)
【解决方案2】:

对此我可能有点晚了,但我想使用 Lodash debounce 添加到上面的一些答案。在对方法进行去抖动时,debounce 函数允许您根据某些事件取消对您的方法的调用。功能组件见示例:

const [isHovered, setIsHovered] = React.useState(false)

const debouncedHandleMouseEnter = debounce(() => setIsHovered(true), 500)

const handlOnMouseLeave = () => {
  setIsHovered(false)
  debouncedHandleMouseEnter.cancel()
}

return (
  <div
    onMouseEnter={debouncedHandleMouseEnter}
    onMouseLeave={handlOnMouseLeave}
  >
   ... do something with isHovered state...
  </div>
)

此示例允许您仅在用户在您的元素中悬停 500 毫秒时调用您的函数,如果鼠标离开该元素,则调用被取消。

【讨论】:

    【解决方案3】:

    您可以使用debounce作为专用包或从lodash等处获取:

    对于实现仅应在重复操作完成后发生的行为很有用。

    const debounce = require('debounce');
    
    class YourComponent extends Component {
      constructor(props) {
        super(props);
    
        this.debouncedMouseOver = debounce(handleMouseOver, 200);
      }
    
      handleMouseOver = data => this.setState({ data });
    
      render() {
        const data = [];
        return <ListElement onMouseOver={() => this.debouncedMouseOver(data)}>Data</ListElement>;
      }
    }
    

    【讨论】:

      【解决方案4】:

      您可以创建一个在匹配特殊要求时触发onMouseOver 事件的方法。

      在另一个示例中,它在500 ms 之后触发。

      /**
       * Hold the descriptor to the setTimeout
       */
      protected timeoutOnMouseOver = false;
      
      /**
       * Method which is going to trigger the onMouseOver only once in Xms
       */
      protected mouseOverTreatment(data) {
         // If they were already a programmed setTimeout
         // stop it, and run a new one
         if (this.timeoutOnMouseOver) {
           clearTimeout(this.timeoutOnMouseOver);
         }
      
         this.timeoutOnMouseOver = setTimeout(() => {
            this.setState(data);
      
            this.timeoutOnMouseOver = false;
         }, 500);
      }
      

      【讨论】:

        【解决方案5】:

        如果您想在某个时间范围内限制操作,debounce 始终是答案。

        实现简单,无需外部库。

        实施:

        type Fnc = (...args: any[]) => void;
        
        // default 300ms delay
        export function debounce<F extends Fnc>(func: F, delay = 300) {
            type Args = F extends (...args: infer P) => void ? P : never;
            let timeout: any;
            return function (this: any, ...args: Args) {
                clearTimeout(timeout);
                timeout = setTimeout(() => {
                    func.apply(this, args);
                }, delay);
            };
        }
        

        用法:

        ...
        
        /** any action you want to debounce */
        function foo(
            data: any, 
            event: React.MouseEvent<HTMLDivElement, MouseEvent>
        ): void {
            this.setState({data});
        }
        
        const fooDebounced = debounce(foo, 500);
        
        <ListElement onMouseOver={fooDebounced.bind(null, data)}>
            Data
        </ListElement>
        ...
        

        您实际上不必bind 一个函数,但如果您循环遍历多个元素以避免为每个元素初始化一个新函数,这是一个好习惯。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-06-07
          • 2019-08-27
          • 1970-01-01
          • 2016-01-10
          • 2019-04-30
          • 1970-01-01
          • 2017-02-22
          相关资源
          最近更新 更多