Skip to content

Commit

Permalink
feat(react): 新增 useHover
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Jul 22, 2020
1 parent 955a3a3 commit 59b4e38
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/react/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

export * from 'react-use'

// @index(['./**/*.ts', '!./**/*.{test,taro}.*', '!./{useToggle,createGlobalState,useTitle,useInterval,useSearchParam,useLocalStorage,useWindowSize}.*'], f => `export * from '${f.path}'`)
// @index(['./**/*.ts', '!./**/*.{test,taro}.*', '!./{useToggle,createGlobalState,useTitle,useInterval,useSearchParam,useLocalStorage,useWindowSize,useHover}.*'], f => `export * from '${f.path}'`)
export * from './defineComponent'
export * from './ExtendComponentProps'
export * from './isVisibleValue'
Expand All @@ -35,3 +35,4 @@ export { useInterval, UseIntervalResult } from './useInterval'
export { useSearchParam } from './useSearchParam'
export { useLocalStorage, UseLocalStorageResult } from './useLocalStorage'
export { useWindowSize } from './useWindowSize'
export { useHover, UseHoverOptions, UseHoverResult } from './useHover'
29 changes: 29 additions & 0 deletions src/react/useHover.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { act, renderHook } from '@testing-library/react-hooks'
import { useHover } from './useHover'
import { wait } from '../utils'

describe('useHover', () => {
test('表现正常', async () => {
const { result } = renderHook(() =>
useHover({
hoverStartDelay: 10,
hoverEndDelay: 20,
}),
)

// 初始悬停态为 false
expect(result.current.hovering).toBeFalse()

// 悬停开始被触发后会经过指定的时间后开启悬停态
act(() => result.current.startHover())
expect(result.current.hovering).toBeFalse()
await wait(15)
expect(result.current.hovering).toBeTrue()

// 悬停结束被触发后会经过指定的时间后关闭悬停态
act(() => result.current.endHover())
expect(result.current.hovering).toBeTrue()
await wait(25)
expect(result.current.hovering).toBeFalse()
})
})
91 changes: 91 additions & 0 deletions src/react/useHover.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { useCallback, useRef, useState } from 'react'
import { useLatest, useUnmount } from 'react-use'

export interface UseHoverOptions {
/**
* 悬停开始后多久开启悬停态,单位为毫秒。
*
* @default 50
*/
hoverStartDelay?: number

/**
* 悬停结束后多久关闭悬停态,单位为毫秒。
*
* @default 400
*/
hoverEndDelay?: number
}

export interface UseHoverResult {
/**
* 当前的悬停态。
*/
hovering: boolean

/**
* 开始悬停。
*/
startHover: () => any

/**
* 结束悬停。
*/
endHover: () => any
}

export function useHover(options: UseHoverOptions = {}): UseHoverResult {
const { hoverStartDelay = 50, hoverEndDelay = 400 } = options

const [hovering, setHovering] = useState(false)

const latestHoverStartDelay = useLatest(hoverStartDelay)
const latestHoverEndDelay = useLatest(hoverEndDelay)
const latestHovering = useLatest(hovering)

const hoverStartTimer = useRef<any>()
const hoverEndTimer = useRef<any>()

const clearHoverStartTimer = useCallback(() => {
if (hoverStartTimer.current) {
clearTimeout(hoverStartTimer.current)
hoverStartTimer.current = undefined
}
}, [])
const clearHoverEndTimer = useCallback(() => {
if (hoverEndTimer.current) {
clearTimeout(hoverEndTimer.current)
hoverEndTimer.current = undefined
}
}, [])

const startHover = useCallback(() => {
if (!latestHovering.current) {
clearHoverStartTimer()
hoverStartTimer.current = setTimeout(() => {
setHovering(true)
}, latestHoverStartDelay.current)
}
}, [])
const endHover = useCallback(() => {
if (latestHovering.current) {
clearHoverEndTimer()
hoverEndTimer.current = setTimeout(() => {
setHovering(false)
}, latestHoverEndDelay.current)
} else {
clearHoverStartTimer()
}
}, [])

useUnmount(() => {
clearHoverStartTimer()
clearHoverEndTimer()
})

return {
hovering,
startHover,
endHover,
}
}

0 comments on commit 59b4e38

Please sign in to comment.