useHover
zyeon/use-hoverFreeHook
A callback-ref hover hook that exposes a hovered boolean via pointerenter/pointerleave, for hover-driven side effects like prefetching — not for CSS-replaceable visual hover styling.
See it running
Open the demo on zyeon
ui.zyeon.ai/r/use-hoverInstall it
npx shadcn@latest add https://ui.zyeon.ai/r/use-hover.jsonSource
1"use client"23import * as React from "react"45export interface UseHoverResult<T extends HTMLElement> {6 /** Callback ref — attach it to the element you want to track hover on. */7 ref: (node: T | null) => void8 hovered: boolean9}1011/**12 * 用 **callback ref**(不是 `useRef` + `useEffect`)订阅一个元素的悬停状态——13 * 与 `useIntersectionObserver` 同一个理由:元素被条件渲染替换(换 key、换分支)14 * 时,callback ref 由 React 在挂载/替换/卸载时逐次调用,天然知道「现在挂的是15 * 哪个节点」,重新绑定监听器因此是免费的、不需要额外 `useEffect` 去追节点变化。16 *17 * 监听 `pointerenter`/`pointerleave`,而不是 `mouseenter`/`mouseleave`:两者对18 * 鼠标语义等价,但 Pointer Events 是 mouse/pen/touch 的统一入口,多覆盖了触屏19 * 笔(pen)这一类真实存在指针悬停的设备。**触屏手指(touch)不产生真正的“悬20 * 停”**——纯触屏上 `pointerenter` 要么不触发,要么只在 tap 瞬间触发一下就立刻21 * `pointerleave`,这是平台语义使然,不是本 hook 需要 polyfill 的缺陷:调用方22 * 不应该指望 `hovered` 在纯触屏设备上表现得像“按下态”。23 *24 * 生命周期:节点挂载 → 绑定 `pointerenter`/`pointerleave`;`hovered` 只在这两25 * 个(异步触发的)事件回调里 `setState`,渲染期和 callback ref 本体都不同步26 * 触碰它,除了下面这一种情况——节点被替换或组件卸载时,React 用 `null` 调用27 * callback ref,此时用保存的引用对**上一个节点** `removeEventListener`(不能假28 * 设卸载时还能拿到同一个 `node` 参数),并把 `hovered` 收回 `false`,避免下一个29 * 替换上来的节点在真正被悬停之前继续读到上一个节点残留的 `true`。30 *31 * **诚实边界**:纯视觉的 hover 效果(变色、上浮阴影、显隐箭头)用 CSS32 * `:hover` / `group-hover` 就够了,不需要引入这个 hook、也不该为此把逻辑搬进33 * JS 里重新做一遍浏览器已经免费给的东西。这个 hook 只该用在「hover 需要驱动34 * 非样式的副作用」时:预取数据、延迟弹出富预览、悬停播放视频/动画预览。它也35 * **不是**可访问性(a11y)等价物——键盘用户永远不会触发 `pointerenter`;如果36 * 同一个交互也必须在键盘 focus 时触发,需要调用方另外监听 `focus`/`blur` 并入37 * 同一个状态(见组件 Prompt 的 Customization levers)。38 */39// … truncated
Files it writes
More from zyeon
All 517 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| useAsyncuse-async | zyeon | Hooks | Free | no deps | |
| useBroadcastChanneluse-broadcast-channel | zyeon | Hooks | Free | no deps | |
| useClickOutsideuse-click-outside | zyeon | Hooks | Free | no deps | |
| useClipboardPasteuse-clipboard-paste | zyeon | Hooks | Free | no deps | |
| useControllableStateuse-controllable-state | zyeon | Hooks | Free | no deps | |
| useCopyToClipboarduse-copy-to-clipboard | zyeon | Hooks | Free | no deps | |
| useCountdownuse-countdown | zyeon | Hooks | Free | no deps | |
| useDebounceValueuse-debounce-value | zyeon | Hooks | Free | no deps |
