useHover

useHover 구현 중 아래와 같은 현상을 발견했다

다른 코드로 해결했다. 현상을 고민, 기역하기 위해 기록한다.

설명

1. ref 방식 에 hover 상태를 저장하기 전 delay 를 준경우 예상하지 못 한 현상이 발생했다.

Element 에 useHover 적용 후 mouserenter, mouseoleave 를 지속적으로 발생하면 mouserenter 상태를 나타내고 있다.

재현 순서

  1. 연속된 Element 에 useHover 를 적용한다
  2. hover 인 경우 Element 에 background-color를 red 로 (다른 것도 괜찮다) 변경한다
  3. 연속된 Element 에 mouserenter, mouseoleave 를 100ms 정도의 시간 간격으로 5초 정도 발생 시킨다
  4. 마우스를 연석된 Element 에서 mouseoleave 상태로 둔다
  5. background-color 가 red 가 있는지 확인한다

2. property 로 inline event handler 전달 는 방생하지 않는다.

1. ref 방식

import React, {useRef, useState, useEffect} from 'react'

function useHover() {
    const [value, setValue] = useState(false)
    const [target, setTarget] = useState<HTMLDivElement | undefined | any>(undefined)
    const [timeoutId, setTimeoutId] = useState<any>()

    // const ref = useRef(null)

    const handleMouseOver = () => {
        const timeoutId = setTimeout(() => {
            setValue(true)
        }, 100)

        setTimeoutId(timeoutId)
    }
    const handleMouseOut = () => {
        clearTimeout(timeoutId)
        setValue(false)
    }

    useEffect(
        () => {
            // const node: HTMLDivElement = ref.current
            if (target) {
                target.addEventListener('mouseover', handleMouseOver)
                target.addEventListener('mouseout', handleMouseOut)

                return () => {
                    target.removeEventListener('mouseover', handleMouseOver)
                    target.removeEventListener('mouseout', handleMouseOut)
                }
            }
        },
        [target], // Recall only if ref changes
    )

    return [setTarget, value]
}

2. property 로 inline event handler 전달

참고 react-use-hover

import React, {useState} from 'react'

export interface UseHoverOptions {
    mouseEnterDelayMS?: number
    mouseLeaveDelayMS?: number
}

export type HoverProps = Pick<React.HTMLAttributes<HTMLElement>, 'onMouseEnter' | 'onMouseLeave'>

export default function useHover({mouseEnterDelayMS = 100, mouseLeaveDelayMS = 0}: UseHoverOptions = {}): [
    boolean,
    HoverProps,
] {
    const [isHovering, setIsHovering] = useState(false)

    const [mouseEnterTimer, setMouseEnterTimer] = useState<any>()
    const [mouseLeaveTimer, setMouseLeaveTimer] = useState<any>()

    return [
        isHovering,
        {
            onMouseEnter: () => {
                clearTimeout(mouseLeaveTimer)
                const mouseEnterTimer = setTimeout(() => setIsHovering(true), mouseEnterDelayMS)
                setMouseEnterTimer(mouseEnterTimer)
            },
            onMouseLeave: () => {
                clearTimeout(mouseEnterTimer)
                const mouseOutTimer = setTimeout(() => setIsHovering(false), mouseLeaveDelayMS)
                setMouseLeaveTimer(mouseOutTimer)
            },
        },
    ]
}

업데이트: