什么是 ahooks?
ahooks 是一个 React Hooks 库,提供了大量实用的自定义 hooks,帮助开发者更高效地构建 React 应用。其中 DOM 类 hooks 是 ahooks 的一个重要分类,专门用于处理 DOM 相关操作,如事件监听、元素状态、拖拽等。
安装 ahooks
npm install ahooks
DOM 类 hooks 详解
useEventListener – 事件监听器
useEventListener
用于添加事件监听器。
import React, { useState, useRef } from "react";
import { useEventListener } from "ahooks";
import { Card, Button } from "antd";
const UseEventListenerExample = () => {
const [count, setCount] = useState(0);
const buttonRef = useRef(null);
useEventListener(
"click",
() => {
setCount((prev) => prev + 1);
},
{ target: buttonRef }
);
return (
<Card title="useEventListener 事件监听器">
<div style={{ marginBottom: 16 }}>
<p>
<strong>点击次数:</strong> {count}
</p>
</div>
<Button ref={buttonRef}>点击我增加计数</Button>
</Card>
);
};
useClickAway – 点击外部
useClickAway
用于检测点击元素外部的事件。
import { useClickAway } from "ahooks";
import { useRef, useState } from "react";
function Dropdown() {
const [visible, setVisible] = useState(false);
const ref = useRef();
useClickAway(() => {
setVisible(false);
}, ref);
return (
<div ref={ref} style={{ position: "relative" }}>
<button onClick={() => setVisible(!visible)}>下拉菜单</button>
{visible && (
<div
style={{
position: "absolute",
top: "100%",
border: "1px solid #ccc",
}}
>
<div>菜单项 1</div>
<div>菜单项 2</div>
<div>菜单项 3</div>
</div>
)}
</div>
);
}
useDocumentVisibility – 文档可见性
useDocumentVisibility
用于监听文档可见性状态。
import React from "react";
import { useDocumentVisibility } from "ahooks";
import { Card } from "antd";
const UseDocumentVisibilityExample = () => {
const documentVisibility = useDocumentVisibility();
return (
<Card title="useDocumentVisibility 文档可见性">
<div style={{ marginBottom: 16 }}>
<p>
<strong>当前状态:</strong> {documentVisibility}
</p>
<p style={{ fontSize: "12px", color: "#666" }}>
切换标签页或最小化窗口查看状态变化
</p>
</div>
</Card>
);
};
useDrop & useDrag – 拖拽
useDrop
和 useDrag
用于处理拖拽操作。
import React, { useState } from "react";
import { useDrop, useDrag } from "ahooks";
import { Card } from "antd";
const UseDropDragExample = () => {
const [isOver, setIsOver] = useState(false);
const [isDragging, setIsDragging] = useState(false);
const dropRef = useDrop({
onDom: (content) => {
console.log("拖拽内容:", content);
setIsOver(false);
},
onDragEnter: () => setIsOver(true),
onDragLeave: () => setIsOver(false),
});
const dragRef = useDrag({
onDragStart: () => setIsDragging(true),
onDragEnd: () => setIsDragging(false),
});
return (
<Card title="useDrop & useDrag 拖拽">
<div style={{ display: "flex", gap: 16 }}>
<div
ref={dragRef}
draggable
onDragStart={(e) => {
e.dataTransfer.setData("text/plain", "拖拽的内容");
setIsDragging(true);
}}
onDragEnd={() => setIsDragging(false)}
style={{
padding: 16,
backgroundColor: isDragging ? "#e6f7ff" : "#f0f0f0",
border: "2px dashed #d9d9d9",
borderRadius: 4,
cursor: "move",
userSelect: "none",
}}
>
拖拽我
</div>
<div
ref={dropRef}
onDragOver={(e) => e.preventDefault()}
onDrop={(e) => {
e.preventDefault();
const data = e.dataTransfer.getData("text/plain");
console.log("放置的内容:", data);
setIsOver(false);
}}
onDragEnter={(e) => {
e.preventDefault();
setIsOver(true);
}}
onDragLeave={(e) => {
e.preventDefault();
setIsOver(false);
}}
style={{
padding: 16,
backgroundColor: isOver ? "#f6ffed" : "#f0f0f0",
border: "2px dashed #d9d9d9",
borderRadius: 4,
minHeight: 60,
}}
>
放置区域
</div>
</div>
</Card>
);
};
useEventTarget – 事件目标
useEventTarget
用于处理表单输入事件。
import React from "react";
import { useEventTarget } from "ahooks";
import { Card, Input, Button } from "antd";
const UseEventTargetExample = () => {
const [value, { onChange, reset }] = useEventTarget({
initialValue: "",
});
return (
<Card title="useEventTarget 事件目标">
<div style={{ marginBottom: 16 }}>
<Input
value={value}
onChange={onChange}
placeholder="输入内容"
style={{ marginBottom: 8 }}
/>
<p>
<strong>当前值:</strong> {value}
</p>
</div>
<Button onClick={reset}>重置</Button>
</Card>
);
};
useExternal – 外部资源
useExternal
用于动态加载外部资源。
import React, { useState } from "react";
import { useExternal } from "ahooks";
import { Card, Button } from "antd";
const UseExternalExample = () => {
const [path, setPath] = useState("");
const status = useExternal(path);
return (
<Card title="useExternal 外部资源">
<div style={{ marginBottom: 16 }}>
<p>
<strong>状态:</strong> <b>{status}</b>
</p>
<p style={{ fontSize: "12px", color: "#666" }}>
加载 Bootstrap CSS 查看徽章样式效果
</p>
</div>
<div style={{ marginBottom: 16 }}>
<div
className="bd-example flex gap-2"
style={{ wordBreak: "break-word" }}
>
<span className="badge bg-primary">Primary</span>
<span className="badge bg-secondary">Secondary</span>
<span className="badge bg-success">Success</span>
<span className="badge bg-danger">Danger</span>
<span className="badge bg-warning text-dark">Warning</span>
<span className="badge bg-info text-dark">Info</span>
<span className="badge bg-light text-dark">Light</span>
<span className="badge bg-dark">Dark</span>
</div>
</div>
<div>
<Button
onClick={() =>
setPath(
"https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
)
}
style={{ marginRight: 8 }}
>
加载 Bootstrap CSS
</Button>
<Button onClick={() => setPath("")}>卸载</Button>
</div>
</Card>
);
};
useTitle – 页面标题
useTitle
用于动态设置页面标题。
import React, { useState } from "react";
import { useTitle } from "ahooks";
import { Card, Input, Button } from "antd";
const UseTitleExample = () => {
const [title, setTitle] = useState("ahooks 示例");
useTitle(title);
return (
<Card title="useTitle 页面标题">
<div style={{ marginBottom: 16 }}>
<Input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="输入页面标题"
style={{ marginBottom: 8 }}
/>
<p>
<strong>当前标题:</strong> {title}
</p>
</div>
<Button onClick={() => setTitle("ahooks 示例")}>重置标题</Button>
</Card>
);
};
useFavicon – 网站图标
useFavicon
用于动态设置网站图标。
import React, { useState } from "react";
import { useFavicon } from "ahooks";
import { Card, Button } from "antd";
const UseFaviconExample = () => {
const [favicon, setFavicon] = useState("https://ahooks.js.org/favicon.ico");
useFavicon(favicon);
return (
<Card title="useFavicon 网站图标">
<div style={{ marginBottom: 16 }}>
<p>
<strong>当前图标:</strong> {favicon}
</p>
<p style={{ fontSize: "12px", color: "#666" }}>
查看浏览器标签页图标变化
</p>
</div>
<div>
<Button
onClick={() => setFavicon("https://www.google.com/favicon.ico")}
style={{ marginRight: 8 }}
>
设置为 Google 图标
</Button>
<Button onClick={() => setFavicon("https://ahooks.js.org/favicon.ico")}>
恢复默认
</Button>
</div>
</Card>
);
};
useFullscreen – 全屏
useFullscreen
用于控制全屏状态。
import React, { useRef } from "react";
import { useFullscreen } from "ahooks";
import { Card, Button } from "antd";
const UseFullscreenExample = () => {
const ref = useRef(null);
const [isFullscreen, { enterFullscreen, exitFullscreen, toggleFullscreen }] =
useFullscreen(ref);
return (
<Card title="useFullscreen 全屏">
<div style={{ marginBottom: 16 }}>
<p>
<strong>全屏状态:</strong> {isFullscreen ? "是" : "否"}
</p>
</div>
<div
ref={ref}
style={{
padding: 16,
backgroundColor: "#f0f0f0",
border: "1px solid #d9d9d9",
borderRadius: 4,
marginBottom: 16,
}}
>
这个区域可以全屏显示
</div>
<div>
<Button onClick={enterFullscreen} style={{ marginRight: 8 }}>
进入全屏
</Button>
<Button onClick={exitFullscreen} style={{ marginRight: 8 }}>
退出全屏
</Button>
<Button onClick={toggleFullscreen}>切换全屏</Button>
</div>
</Card>
);
};
useHover – 悬停状态
useHover
用于检测元素悬停状态。
import React, { useRef } from "react";
import { useHover } from "ahooks";
import { Card } from "antd";
const UseHoverExample = () => {
const ref = useRef(null);
const isHovering = useHover(ref);
return (
<Card title="useHover 悬停状态">
<div style={{ marginBottom: 16 }}>
<p>
<strong>悬停状态:</strong> {isHovering ? "悬停中" : "未悬停"}
</p>
</div>
<div
ref={ref}
style={{
padding: 16,
backgroundColor: isHovering ? "#e6f7ff" : "#f0f0f0",
border: "1px solid #d9d9d9",
borderRadius: 4,
transition: "background-color 0.3s",
}}
>
鼠标悬停我
</div>
</Card>
);
};
useMutationObserver – 元素变化监听
useMutationObserver
用于监听 DOM 元素变化。
import React, { useState, useRef, useCallback } from "react";
import { useMutationObserver } from "ahooks";
import { Card, Button } from "antd";
const UseMutationObserverExample = () => {
const [changeCount, setChangeCount] = useState(0);
const ref = useRef(null);
const handleMutation = useCallback((mutationsList) => {
mutationsList.forEach(() => setChangeCount((c) => c + 1));
}, []);
useMutationObserver(handleMutation, ref, {
attributes: true,
childList: true,
subtree: true,
});
const addElement = () => {
const div = document.createElement("div");
div.textContent = "新元素";
ref.current?.appendChild(div);
};
const changeAttribute = () => {
if (ref.current) {
ref.current.style.backgroundColor = "#e6f7ff";
}
};
return (
<Card title="useMutationObserver 元素变化监听">
<div style={{ marginBottom: 16 }}>
<p>
<strong>变化次数:</strong> {changeCount}
</p>
</div>
<div
ref={ref}
style={{
padding: 16,
backgroundColor: "#f0f0f0",
border: "1px solid #d9d9d9",
borderRadius: 4,
marginBottom: 16,
minHeight: 60,
}}
>
监听区域
</div>
<div>
<Button onClick={addElement} style={{ marginRight: 8 }}>
添加元素
</Button>
<Button onClick={changeAttribute}>改变属性</Button>
</div>
</Card>
);
};
useInViewport – 视口可见性
useInViewport
用于检测元素是否在视口中可见。
import React, { useRef } from "react";
import { useInViewport } from "ahooks";
import { Card } from "antd";
const UseInViewportExample = () => {
const ref = useRef(null);
const [inViewport] = useInViewport(ref);
return (
<Card title="useInViewport 视口可见性">
<div style={{ marginBottom: 16 }}>
<p>
<strong>可见状态:</strong> {inViewport ? "可见" : "不可见"}
</p>
</div>
<div
style={{ height: 200, overflow: "auto", border: "1px solid #d9d9d9" }}
>
<div style={{ height: 100, backgroundColor: "#f0f0f0" }}>
滚动区域顶部
</div>
<div
ref={ref}
style={{
padding: 16,
backgroundColor: inViewport ? "#f6ffed" : "#fff2e8",
border: "1px solid #d9d9d9",
borderRadius: 4,
margin: 16,
}}
>
监听可见性的元素
</div>
<div style={{ height: 300, backgroundColor: "#f0f0f0" }}>
滚动区域底部
</div>
</div>
</Card>
);
};
useKeyPress – 键盘按键
useKeyPress
用于监听键盘按键事件。
import React, { useState } from "react";
import { useKeyPress } from "ahooks";
import { Card } from "antd";
const UseKeyPressExample = () => {
const [pressedKey, setPressedKey] = useState("");
useKeyPress(["a", "b", "c"], (event) => {
setPressedKey(event.key);
});
return (
<Card title="useKeyPress 键盘按键">
<div style={{ marginBottom: 16 }}>
<p>
<strong>按下的键:</strong> {pressedKey || "无"}
</p>
<p style={{ fontSize: "12px", color: "#666" }}>
按下 A、B、C 键查看效果
</p>
</div>
</Card>
);
};
useLongPress – 长按
useLongPress
用于检测长按事件。
import React, { useState } from "react";
import { useLongPress } from "ahooks";
import { Card } from "antd";
const UseLongPressExample = () => {
const [message, setMessage] = useState("");
const longPress = useLongPress(
() => {
setMessage("长按触发");
},
{
onCancel: () => {
setMessage("长按取消");
},
},
{
delay: 1000,
}
);
return (
<Card title="useLongPress 长按">
<div style={{ marginBottom: 16 }}>
<p>
<strong>状态:</strong> {message || "等待长按"}
</p>
</div>
<div
{...longPress}
style={{
padding: 16,
backgroundColor: "#f0f0f0",
border: "1px solid #d9d9d9",
borderRadius: 4,
cursor: "pointer",
userSelect: "none",
}}
>
长按我 1 秒
</div>
</Card>
);
};
useMouse – 鼠标位置
useMouse
用于获取鼠标位置。
import React from "react";
import { useMouse } from "ahooks";
import { Card } from "antd";
const UseMouseExample = () => {
const mouse = useMouse();
return (
<Card title="useMouse 鼠标位置">
<div style={{ marginBottom: 16 }}>
<p>
<strong>X 坐标:</strong> {mouse.clientX}
</p>
<p>
<strong>Y 坐标:</strong> {mouse.clientY}
</p>
<p>
<strong>屏幕 X:</strong> {mouse.screenX}
</p>
<p>
<strong>屏幕 Y:</strong> {mouse.screenY}
</p>
</div>
<div style={{ fontSize: "12px", color: "#666" }}>
移动鼠标查看坐标变化
</div>
</Card>
);
};
useResponsive – 响应式
useResponsive
用于检测屏幕尺寸。
import React from "react";
import { configResponsive, useResponsive } from "ahooks";
import { Card } from "antd";
configResponsive({
small: 0,
middle: 800,
large: 1200,
});
const UseResponsiveExample = () => {
const responsive = useResponsive();
return (
<Card title="useResponsive 响应式">
<div style={{ marginBottom: 16 }}>
<p>请调整浏览器窗口宽度查看效果:</p>
{Object.keys(responsive).map((key) => (
<p key={key}>
<strong>{key}:</strong> {responsive[key] ? "✔" : "✘"}
</p>
))}
</div>
<div style={{ fontSize: "12px", color: "#666" }}>
断点配置: small(0px) | middle(800px) | large(1200px)
</div>
</Card>
);
};
useScroll – 滚动
useScroll
用于监听滚动事件。
import React, { useRef } from "react";
import { useScroll } from "ahooks";
import { Card } from "antd";
const UseScrollExample = () => {
const ref = useRef(null);
const scroll = useScroll(ref);
return (
<Card title="useScroll 滚动">
<div style={{ marginBottom: 16 }}>
<p>
<strong>滚动位置:</strong> {scroll?.top || 0}
</p>
<p>
<strong>滚动方向:</strong> {scroll?.direction || "无"}
</p>
</div>
<div
ref={ref}
style={{
height: 200,
overflow: "auto",
border: "1px solid #d9d9d9",
padding: 16,
}}
>
{Array.from({ length: 20 }, (_, i) => (
<div
key={i}
style={{ padding: 8, borderBottom: "1px solid #f0f0f0" }}
>
滚动内容 {i + 1}
</div>
))}
</div>
</Card>
);
};
useSize – 元素尺寸
useSize
用于监听元素尺寸变化。
import React, { useRef } from "react";
import { useSize } from "ahooks";
import { Card, Button } from "antd";
const UseSizeExample = () => {
const ref = useRef(null);
const size = useSize(ref);
const toggleSize = () => {
if (ref.current) {
ref.current.style.width =
ref.current.style.width === "200px" ? "300px" : "200px";
ref.current.style.height =
ref.current.style.height === "100px" ? "150px" : "100px";
}
};
return (
<Card title="useSize 元素尺寸">
<div style={{ marginBottom: 16 }}>
<p>
<strong>宽度:</strong> {size?.width}px
</p>
<p>
<strong>高度:</strong> {size?.height}px
</p>
</div>
<div
ref={ref}
style={{
width: 200,
height: 100,
backgroundColor: "#f0f0f0",
border: "1px solid #d9d9d9",
borderRadius: 4,
marginBottom: 16,
transition: "all 0.3s",
}}
>
监听尺寸的元素
</div>
<Button onClick={toggleSize}>切换尺寸</Button>
</Card>
);
};
useFocusWithin – 焦点状态
useFocusWithin
用于检测元素或其子元素是否获得焦点。
import React, { useRef } from "react";
import { useFocusWithin } from "ahooks";
import { Card, Input, Button, message } from "antd";
const UseFocusWithinExample = () => {
const ref = useRef(null);
const isFocusWithin = useFocusWithin(ref, {
onFocus: () => {
message.info("获得焦点");
},
onBlur: () => {
message.info("失去焦点");
},
});
return (
<Card title="useFocusWithin 焦点状态">
<div style={{ marginBottom: 16 }}>
<p>
<strong>焦点状态:</strong> {JSON.stringify(isFocusWithin)}
</p>
</div>
<div
ref={ref}
style={{
padding: 16,
backgroundColor: isFocusWithin ? "#f6ffed" : "#f0f0f0",
border: "1px solid #d9d9d9",
borderRadius: 4,
transition: "background-color 0.3s",
}}
>
<Input placeholder="点击我" style={{ marginBottom: 8 }} />
<Button>按钮</Button>
</div>
</Card>
);
};
DOM 类 hooks 速查表
Hook 名称 | 用途 | 描述 |
---|---|---|
useEventListener | 事件监听器 | 添加事件监听器 |
useClickAway | 点击外部 | 检测点击元素外部的事件 |
useDocumentVisibility | 文档可见性 | 监听文档可见性状态 |
useDrop & useDrag | 拖拽 | 处理拖拽操作 |
useEventTarget | 事件目标 | 处理表单输入事件 |
useExternal | 外部资源 | 动态加载外部资源 |
useTitle | 页面标题 | 动态设置页面标题 |
useFavicon | 网站图标 | 动态设置网站图标 |
useFullscreen | 全屏 | 控制全屏状态 |
useHover | 悬停状态 | 检测元素悬停状态 |
useMutationObserver | 元素变化监听 | 监听 DOM 元素变化 |
useInViewport | 视口可见性 | 检测元素是否在视口中可见 |
useKeyPress | 键盘按键 | 监听键盘按键事件 |
useLongPress | 长按 | 检测长按事件 |
useMouse | 鼠标位置 | 获取鼠标位置 |
useResponsive | 响应式 | 检测屏幕尺寸 |
useScroll | 滚动 | 监听滚动事件 |
useSize | 元素尺寸 | 监听元素尺寸变化 |
useFocusWithin | 焦点状态 | 检测元素或其子元素是否获得焦点 |
原文链接:https://code.ifrontend.net/archives/789,转载请注明出处。
评论0