什么是 ahooks?
ahooks 是一个 React Hooks 库,提供了大量实用的自定义 hooks,帮助开发者更高效地构建 React 应用。其中场景类 hooks 是 ahooks 的一个重要分类,专门针对特定业务场景提供解决方案。
安装 ahooks
npm install ahooks
场景类 hook 详解
useAntdTable – Antd 表格集成
useAntdTable
用于处理表格与表单的联动场景。
import React from "react";
import { useAntdTable } from "ahooks";
import { Form, Input, Button, Table } from "antd";
const UserTable = () => {
const [form] = Form.useForm();
const { tableProps, search } = useAntdTable(
async (params, form) => {
const { current, pageSize } = params;
const response = await fetch("/api/users", {
method: "POST",
body: JSON.stringify({
page: current,
size: pageSize,
...form,
}),
});
const data = await response.json();
return {
list: data.list,
total: data.total,
};
},
{
form,
defaultPageSize: 10,
}
);
return (
<div className="container m-4">
<Form form={form} layout="inline" className="mb-2">
<Form.Item name="name" label="姓名">
<Input placeholder="请输入姓名" />
</Form.Item>
<Form.Item name="email" label="邮箱">
<Input placeholder="请输入邮箱" />
</Form.Item>
<Form.Item>
<Button type="primary" onClick={search.submit}>
搜索
</Button>
<Button onClick={search.reset} style={{ marginLeft: 8 }}>
重置
</Button>
</Form.Item>
</Form>
<Table
{...tableProps}
columns={[
{ title: "姓名", dataIndex: "name" },
{ title: "邮箱", dataIndex: "email" },
{ title: "创建时间", dataIndex: "createTime" },
]}
/>
</div>
);
};
useFusionTable – Fusion 表格集成
useFusionTable
用于处理表格与表单的联动场景。
import React from "react";
import { useFusionTable } from "ahooks";
import { Table, Button, Input } from "@alifd/next";
const UserTable = () => {
const { tableProps, search, loading } = useFusionTable(
async (params) => {
const { current, pageSize, ...rest } = params;
const response = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
page: current,
size: pageSize,
...rest,
}),
});
const data = await response.json();
return {
list: data.list,
total: data.total,
};
},
{
defaultPageSize: 10,
defaultParams: [{ current: 1, pageSize: 10 }],
}
);
return (
<div>
<div style={{ marginBottom: 16 }}>
<Input
placeholder="搜索用户名"
onChange={(value) => search.setFieldValue("name", value)}
style={{ width: 200, marginRight: 8 }}
/>
<Button type="primary" onClick={search.submit}>
搜索
</Button>
<Button onClick={search.reset} style={{ marginLeft: 8 }}>
重置
</Button>
</div>
<Table {...tableProps} loading={loading}>
<Table.Column title="姓名" dataIndex="name" />
<Table.Column title="邮箱" dataIndex="email" />
<Table.Column title="创建时间" dataIndex="createTime" />
</Table>
</div>
);
};
useInfiniteScroll – 无限滚动
import React from "react";
import { useInfiniteScroll } from "ahooks";
const InfiniteList = () => {
const { data, loading, noMore } = useInfiniteScroll(
async (d) => {
const { list = [], total = 0 } = await fetchData(d?.list?.length || 0);
return {
list: [...(d?.list || []), ...list],
total,
};
},
{
target: document,
isNoMore: (d) => d?.list?.length >= d?.total,
threshold: 100,
}
);
return (
<div style={{ height: "100vh", overflow: "auto" }}>
{data?.list?.map((item, index) => (
<div
key={`${item.id}-${index}`}
style={{
padding: "12px",
borderBottom: "1px solid #eee",
backgroundColor: index % 2 === 0 ? "#f9f9f9" : "white",
}}
>
<h3>{item.title}</h3>
<p>{item.description}</p>
</div>
))}
{loading && (
<div style={{ textAlign: "center", padding: "20px" }}>加载中...</div>
)}
{noMore && (
<div style={{ textAlign: "center", padding: "20px", color: "#999" }}>
没有更多数据了
</div>
)}
</div>
);
};
// 模拟数据获取函数
const fetchData = async (offset = 0) => {
// 模拟API调用
await new Promise((resolve) => setTimeout(resolve, 1000));
const pageSize = 10;
const mockData = Array.from({ length: pageSize }, (_, i) => ({
id: offset + i,
title: `项目 ${offset + i + 1}`,
description: `这是第 ${offset + i + 1} 个项目的描述`,
}));
return {
list: mockData,
total: 50, // 总共50条数据
};
};
export default InfiniteList;
usePagination – 分页管理
import React from "react";
import { usePagination } from "ahooks";
import { Button, Input } from "antd";
const PaginationExample = () => {
const { data, loading, pagination, run } = usePagination(
async ({ current, pageSize }) => {
const response = await fetch(
`/api/users?page=${current}&size=${pageSize}`
);
return response.json();
},
{
defaultPageSize: 5,
defaultCurrent: 1,
}
);
const [searchValue, setSearchValue] = React.useState("");
const handleSearch = () => {
run({ current: 1, pageSize: pagination.pageSize, search: searchValue });
};
return (
<div>
<div style={{ marginBottom: 16 }}>
<Input
placeholder="搜索用户"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
style={{ width: 200, marginRight: 8 }}
onPressEnter={handleSearch}
/>
<Button type="primary" onClick={handleSearch}>
搜索
</Button>
</div>
{loading ? (
<div>加载中...</div>
) : (
<div>
{data?.list?.map((user) => (
<div
key={user.id}
style={{
padding: "8px",
border: "1px solid #ddd",
marginBottom: "8px",
}}
>
<strong>{user.name}</strong> - {user.email}
</div>
))}
<div style={{ marginTop: 16 }}>
<Button
disabled={pagination.current === 1}
onClick={() => pagination.changeCurrent(pagination.current - 1)}
>
上一页
</Button>
<span style={{ margin: "0 16px" }}>
第 {pagination.current} 页,共 {pagination.total} 条
</span>
<Button
disabled={pagination.current >= pagination.totalPages}
onClick={() => pagination.changeCurrent(pagination.current + 1)}
>
下一页
</Button>
</div>
</div>
)}
</div>
);
};
useDynamicList – 动态列表
import React from "react";
import { useDynamicList } from "ahooks";
import { Button, Input, Card } from "antd";
const DynamicListExample = () => {
const { list, remove, getKey, insert, move, replace, reset } = useDynamicList(
[
{ name: "张三", age: 25 },
{ name: "李四", age: 30 },
{ name: "王五", age: 28 },
]
);
const [inputName, setInputName] = React.useState("");
const [inputAge, setInputAge] = React.useState("");
const handleAdd = () => {
if (inputName && inputAge) {
insert(0, { name: inputName, age: parseInt(inputAge) });
setInputName("");
setInputAge("");
}
};
return (
<div>
<div style={{ marginBottom: 16 }}>
<Input
placeholder="姓名"
value={inputName}
onChange={(e) => setInputName(e.target.value)}
style={{ width: 120, marginRight: 8 }}
/>
<Input
placeholder="年龄"
value={inputAge}
onChange={(e) => setInputAge(e.target.value)}
style={{ width: 80, marginRight: 8 }}
/>
<Button type="primary" onClick={handleAdd}>
添加到开头
</Button>
<Button onClick={reset} style={{ marginLeft: 8 }}>
重置
</Button>
</div>
{list.map((item, index) => (
<Card
key={getKey(index)}
size="small"
style={{ marginBottom: 8 }}
extra={
<div>
<Button
size="small"
onClick={() => move(index, index - 1)}
disabled={index === 0}
>
上移
</Button>
<Button
size="small"
onClick={() => move(index, index + 1)}
disabled={index === list.length - 1}
style={{ marginLeft: 4 }}
>
下移
</Button>
<Button
size="small"
danger
onClick={() => remove(index)}
style={{ marginLeft: 4 }}
>
删除
</Button>
</div>
}
>
<p>
<strong>姓名:</strong> {item.name}
</p>
<p>
<strong>年龄:</strong> {item.age}
</p>
</Card>
))}
</div>
);
};
useVirtualList – 虚拟列表
import React, { useMemo, useRef } from "react";
import { useVirtualList } from "ahooks";
export default function Demo() {
const containerRef = useRef(null);
const wrapperRef = useRef(null);
// 生成大量测试数据
const originalList = useMemo(() => {
return Array.from({ length: 10000 }, (_, index) => ({
id: index,
title: `列表项 ${index + 1}`,
content: `这是第 ${index + 1} 个列表项的内容,包含一些示例文本。`,
timestamp: new Date(
Date.now() - Math.random() * 10000000000
).toLocaleString(),
}));
}, []);
// 使用 useVirtualList hook - 正确版本
const [list] = useVirtualList(originalList, {
containerTarget: containerRef,
wrapperTarget: wrapperRef,
itemHeight: 80,
overscan: 10,
});
console.log("originalList length:", originalList.length);
console.log("virtual list length:", list.length);
console.log("containerRef:", containerRef);
console.log("wrapperRef:", wrapperRef);
// 计算总高度
const totalHeight = originalList.length * 80; // 每个项目80px高度
console.log("totalHeight:", totalHeight);
console.log("list first item:", list?.[0]);
// 如果虚拟列表不工作,先显示普通列表
const showNormalList = !list || list.length === 0;
return (
<div className="p-6 max-w-4xl mx-auto">
<h1 className="text-3xl font-bold text-gray-800 mb-6">
useVirtualList 虚拟列表示例
</h1>
<div className="mb-4 text-sm text-gray-600">
总共 {originalList.length} 个列表项,但只渲染可见区域的项目
{showNormalList && (
<span className="text-orange-600"> (使用普通列表作为备用)</span>
)}
</div>
{/* 虚拟列表容器 */}
<div
ref={containerRef}
className="border border-gray-200 rounded-lg overflow-y-auto bg-white shadow-sm"
style={{ height: "600px" }}
>
<div ref={wrapperRef}>
{showNormalList
? // 备用:普通列表
originalList.slice(0, 20).map((item) => (
<div
key={item.id}
className="border-b border-gray-100 p-4 hover:bg-gray-50 transition-colors"
style={{ height: "80px" }}
>
<div className="flex items-center justify-between">
<div className="flex-1">
<h3 className="font-semibold text-gray-800 mb-1">
{item.title}
</h3>
<p className="text-sm text-gray-600 line-clamp-2">
{item.content}
</p>
</div>
<div className="text-xs text-gray-400 ml-4">
{item.timestamp}
</div>
</div>
</div>
))
: // 虚拟列表
list.map((item) => (
<div
key={item.index}
className="border-b border-gray-100 p-4 hover:bg-gray-50 transition-colors"
style={{ height: "80px" }}
>
<div className="flex items-center justify-between">
<div className="flex-1">
<h3 className="font-semibold text-gray-800 mb-1">
{item.data.title}
</h3>
<p className="text-sm text-gray-600 line-clamp-2">
{item.data.content}
</p>
</div>
<div className="text-xs text-gray-400 ml-4">
{item.data.timestamp}
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
}
6. useHistoryTravel – 历史记录
import React from "react";
import { useHistoryTravel } from "ahooks";
import { Button, Input, Card } from "antd";
const HistoryTravelExample = () => {
const { value, setValue, backLength, forwardLength, back, forward, reset } =
useHistoryTravel("");
const handleChange = (e) => {
setValue(e.target.value);
};
return (
<div>
<Card title="历史记录管理" style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 16 }}>
<Input
value={value}
onChange={handleChange}
placeholder="输入内容,每次修改都会记录历史"
style={{ marginBottom: 8 }}
/>
<div>
<Button onClick={reset} style={{ marginRight: 8 }}>
重置
</Button>
<span style={{ color: "#666" }}>当前值: {value || "(空)"}</span>
</div>
</div>
<div style={{ marginBottom: 16 }}>
<Button
disabled={backLength <= 0}
onClick={back}
style={{ marginRight: 8 }}
>
后退 ({backLength})
</Button>
<Button
disabled={forwardLength <= 0}
onClick={forward}
style={{ marginRight: 8 }}
>
前进 ({forwardLength})
</Button>
</div>
</Card>
<Card title="历史记录信息">
<p>
<strong>可后退步数:</strong> {backLength}
</p>
<p>
<strong>可前进步数:</strong> {forwardLength}
</p>
<p>
<strong>总历史记录数:</strong> {backLength + forwardLength + 1}
</p>
</Card>
</div>
);
};
useNetwork – 网络状态
import React from "react";
import { useNetwork } from "ahooks";
import { Card, Tag, Alert } from "antd";
const NetworkExample = () => {
const network = useNetwork();
const getNetworkStatus = () => {
if (network.online) {
return <Tag color="green">在线</Tag>;
}
return <Tag color="red">离线</Tag>;
};
const getNetworkType = () => {
if (network.effectiveType) {
return <Tag color="blue">{network.effectiveType}</Tag>;
}
return <Tag color="orange">未知</Tag>;
};
return (
<div>
<Card title="网络状态监控">
<div style={{ marginBottom: 16 }}>
<strong>连接状态:</strong> {getNetworkStatus()}
</div>
<div style={{ marginBottom: 16 }}>
<strong>网络类型:</strong> {getNetworkType()}
</div>
{!network.online && (
<Alert
message="网络连接已断开"
description="请检查您的网络连接,某些功能可能无法正常使用。"
type="warning"
showIcon
style={{ marginBottom: 16 }}
/>
)}
<Card title="详细网络信息" size="small">
<p>
<strong>在线状态:</strong> {network.online ? "是" : "否"}
</p>
<p>
<strong>网络类型:</strong> {network.effectiveType || "未知"}
</p>
<p>
<strong>下行速度:</strong>{" "}
{network.downlink ? `${network.downlink} Mbps` : "未知"}
</p>
<p>
<strong>往返时间:</strong>{" "}
{network.rtt ? `${network.rtt} ms` : "未知"}
</p>
<p>
<strong>保存数据模式:</strong> {network.saveData ? "是" : "否"}
</p>
</Card>
</Card>
</div>
);
};
useSelections – 多选管理
import React, { useMemo } from "react";
import { useSelections } from "ahooks";
import { Checkbox, Button, Card, List } from "antd";
const SelectionsExample = () => {
const list = useMemo(
() => [
{ id: 1, name: "苹果", price: 5.5 },
{ id: 2, name: "香蕉", price: 3.2 },
{ id: 3, name: "橙子", price: 4.8 },
{ id: 4, name: "葡萄", price: 8.9 },
{ id: 5, name: "草莓", price: 12.5 },
{ id: 6, name: "蓝莓", price: 15.8 },
],
[]
);
const {
selected,
allSelected,
isSelected,
toggle,
toggleAll,
partiallySelected,
} = useSelections(list, {
defaultSelected: [list[0], list[2]], // 默认选中第一个和第三个项
});
const totalPrice = selected
.map((item) => item.price || 0)
.reduce((sum, price) => sum + price, 0);
// 调试信息
console.log("selected:", selected);
console.log("allSelected:", allSelected);
console.log("partiallySelected:", partiallySelected);
console.log("list:", list);
return (
<div>
<div
style={{
marginBottom: 16,
padding: "10px",
backgroundColor: "#f5f5f5",
borderRadius: "4px",
}}
>
<strong>当前选中项 ID:</strong>{" "}
{selected.map((item) => item.id).join(", ") || "无"}
</div>
<Card title="多选管理示例" style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 16 }}>
<Checkbox
checked={allSelected}
onClick={toggleAll}
indeterminate={partiallySelected}
style={{ marginRight: 8 }}
>
全选
</Checkbox>
<span style={{ color: "#666" }}>
已选择 {selected.length} 项,总价: ¥{totalPrice.toFixed(2)}
</span>
</div>
<List
dataSource={list}
renderItem={(item) => (
<List.Item
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "8px 0",
borderBottom: "1px solid #f0f0f0",
}}
>
<div style={{ display: "flex", alignItems: "center" }}>
<Checkbox
checked={isSelected(item)}
onClick={() => toggle(item)}
style={{ marginRight: 8 }}
/>
<span>{item.name}</span>
</div>
<span style={{ color: "#666" }}>¥{item.price}</span>
</List.Item>
)}
/>
</Card>
</div>
);
};
useCountDown – 倒计时
import React, { useState } from "react";
import { useCountDown } from "ahooks";
import { Button, Card, Input, message } from "antd";
const SmsCountDownExample = () => {
const [phoneNumber, setPhoneNumber] = useState("");
const [targetDate, setTargetDate] = useState();
const [countdown] = useCountDown({
targetDate,
onEnd: () => {
console.log("倒计时结束!");
message.success("倒计时结束,可以重新发送短信");
},
});
const formatTime = (ms) => {
const seconds = Math.floor(ms / 1000);
return seconds.toString().padStart(2, "0");
};
const handleSendSms = () => {
if (!phoneNumber) {
message.error("请输入手机号码");
return;
}
if (phoneNumber.length !== 11) {
message.error("请输入正确的11位手机号码");
return;
}
// 模拟发送短信
message.success(`验证码已发送到 ${phoneNumber}`);
// 开始60秒倒计时
setTargetDate(Date.now() + 60000);
};
const handleReset = () => {
setTargetDate(undefined);
};
return (
<div>
<Card title="短信验证码倒计时" style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 16 }}>
<Input
placeholder="请输入手机号码"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
style={{ marginBottom: 8 }}
maxLength={11}
/>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<Button
type="primary"
onClick={handleSendSms}
disabled={countdown !== 0}
style={{ flex: 1 }}
>
{countdown === 0
? "发送验证码"
: `重新发送(${formatTime(countdown)}s)`}
</Button>
{countdown !== 0 && (
<Button onClick={handleReset} size="small">
重置
</Button>
)}
</div>
</div>
</Card>
</div>
);
};
useCounter – 计数器
import React from "react";
import { useCounter } from "ahooks";
import { Button, Card, InputNumber, Space } from "antd";
const CounterExample = () => {
const [current, { inc, dec, set, reset }] = useCounter(1, {
min: 1,
max: 10,
});
return (
<div>
<Card title="useCounter 计数器示例" style={{ marginBottom: 16 }}>
<div style={{ textAlign: "center", marginBottom: 16 }}>
<div
style={{ fontSize: "32px", fontWeight: "bold", color: "#1890ff" }}
>
{current}
</div>
</div>
<div style={{ textAlign: "center", marginBottom: 16 }}>
<Space>
<Button
type="primary"
onClick={() => inc()}
style={{ marginRight: 8 }}
>
inc(1)
</Button>
<Button onClick={() => dec()} style={{ marginRight: 8 }}>
dec(1)
</Button>
<Button onClick={() => set(3)} style={{ marginRight: 8 }}>
set(3)
</Button>
<Button onClick={reset} style={{ marginRight: 8 }}>
reset(0)
</Button>
</Space>
</div>
<div style={{ textAlign: "center" }}>
<Space>
<InputNumber
min={1}
max={10}
value={current}
onChange={(value) => set(value || 1)}
style={{ width: 100 }}
/>
<span style={{ color: "#666" }}>直接输入数值</span>
</Space>
</div>
</Card>
</div>
);
};
useTextSelection – 文本选择
import React from "react";
import { useTextSelection } from "ahooks";
import { Card } from "antd";
const TextSelectionExample = () => {
const selection = useTextSelection();
return (
<div>
<Card title="文本选择监听" style={{ marginBottom: 16 }}>
<div
style={{
padding: "16px",
border: "1px solid #d9d9d9",
borderRadius: "6px",
backgroundColor: "#fafafa",
lineHeight: "1.8",
}}
>
<p>
这是一段示例文本,您可以在这里选择任意内容。选择文本后,下方会显示选择的相关信息,
包括选中的文本内容、选择的位置信息等。这个功能在需要获取用户选择的文本内容时非常有用,
比如实现文本高亮、复制选中内容等功能。
</p>
<p>
您可以尝试选择单个单词、短语或者整段文本,观察下方信息的变化。
选择不同的文本内容,会得到不同的选择结果。
</p>
</div>
</Card>
<Card title="选择信息" size="small" style={{ marginBottom: 16 }}>
{selection.text ? (
<div>
<p>
<strong>选中的文本:</strong>
</p>
<div
style={{
padding: "8px",
backgroundColor: "#f0f0f0",
borderRadius: "4px",
marginBottom: "8px",
wordBreak: "break-all",
}}
>
"{selection.text}"
</div>
{selection.rects && selection.rects.length > 0 && (
<div>
<p>
<strong>选择区域:</strong>
</p>
<div style={{ fontSize: "12px", color: "#666" }}>
<p>区域数量: {selection.rects.length}</p>
{selection.rects.map((rect, index) => (
<p key={index}>
区域 {index + 1}: x={rect.x.toFixed(0)}, y=
{rect.y.toFixed(0)}, 宽={rect.width.toFixed(0)}, 高=
{rect.height.toFixed(0)}
</p>
))}
</div>
</div>
)}
</div>
) : (
<div style={{ color: "#999", textAlign: "center" }}>
请在上方文本中选择内容
</div>
)}
</Card>
</div>
);
};
useWebSocket – WebSocket 连接
import React, { useState } from "react";
import { useWebSocket } from "ahooks";
import { Button, Card, Input, List, Tag, Alert } from "antd";
const WebSocketExample = () => {
const [url, setUrl] = useState("ws://localhost:8080");
const [message, setMessage] = useState("");
const { readyState, sendMessage, latestMessage, disconnect, connect } =
useWebSocket(url, {
onOpen: () => {
console.log("WebSocket连接成功");
},
onMessage: (message) => {
console.log("收到消息:", message);
},
onError: (error) => {
console.log("WebSocket错误:", error);
},
onClose: () => {
console.log("WebSocket连接关闭");
},
manual: true, // 手动连接
});
const [messageHistory, setMessageHistory] = useState([]);
React.useEffect(() => {
if (latestMessage) {
setMessageHistory((prev) => [
...prev,
{
id: Date.now(),
content: latestMessage.data,
type: "received",
time: new Date().toLocaleTimeString(),
},
]);
}
}, [latestMessage]);
const handleSend = () => {
if (message.trim()) {
sendMessage(message);
setMessageHistory((prev) => [
...prev,
{
id: Date.now(),
content: message,
type: "sent",
time: new Date().toLocaleTimeString(),
},
]);
setMessage("");
}
};
const getStatusText = () => {
switch (readyState) {
case 0:
return { text: "连接中", color: "processing" };
case 1:
return { text: "已连接", color: "success" };
case 2:
return { text: "关闭中", color: "warning" };
case 3:
return { text: "已关闭", color: "error" };
default:
return { text: "未知", color: "default" };
}
};
const status = getStatusText();
return (
<div>
<Card title="WebSocket 连接管理" style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 16 }}>
<Input
value={url}
onChange={(e) => setUrl(e.target.value)}
placeholder="WebSocket URL"
style={{ marginBottom: 8 }}
/>
<div>
<Button
type="primary"
onClick={connect}
disabled={readyState === 1}
style={{ marginRight: 8 }}
>
连接
</Button>
<Button
onClick={disconnect}
disabled={readyState !== 1}
style={{ marginRight: 8 }}
>
断开
</Button>
<Tag color={status.color}>状态: {status.text}</Tag>
</div>
</div>
{readyState === 3 && (
<Alert
message="连接已断开"
description="请点击连接按钮重新建立WebSocket连接"
type="warning"
showIcon
style={{ marginBottom: 16 }}
/>
)}
</Card>
<Card title="消息收发" style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 16 }}>
<Input.TextArea
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="输入要发送的消息"
rows={3}
style={{ marginBottom: 8 }}
onPressEnter={(e) => {
if (!e.shiftKey) {
e.preventDefault();
handleSend();
}
}}
/>
<Button
type="primary"
onClick={handleSend}
disabled={readyState !== 1 || !message.trim()}
>
发送消息
</Button>
</div>
<List
size="small"
dataSource={messageHistory}
renderItem={(item) => (
<List.Item
style={{
padding: "8px 0",
borderBottom: "1px solid #f0f0f0",
}}
>
<div style={{ width: "100%" }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: "4px",
}}
>
<Tag color={item.type === "sent" ? "blue" : "green"}>
{item.type === "sent" ? "发送" : "接收"}
</Tag>
<span style={{ fontSize: "12px", color: "#666" }}>
{item.time}
</span>
</div>
<div
style={{
padding: "8px",
backgroundColor:
item.type === "sent" ? "#e6f7ff" : "#f6ffed",
borderRadius: "4px",
wordBreak: "break-all",
}}
>
{item.content}
</div>
</div>
</List.Item>
)}
/>
</Card>
<Card title="连接信息" size="small">
<p>
<strong>连接状态:</strong>
<Tag color={status.color}>{status.text}</Tag>
</p>
<p>
<strong>连接URL:</strong> {url}
</p>
<p>
<strong>消息总数:</strong> {messageHistory.length}
</p>
<p>
<strong>最后消息:</strong> {latestMessage?.data || "无"}
</p>
</Card>
</div>
);
};
useTheme – 主题
import React from "react";
import { useTheme } from "ahooks";
import { Button, Card, Tag } from "antd";
const ThemeExample = () => {
const { theme, themeMode, setThemeMode } = useTheme({
localStorageKey: "themeMode",
});
return (
<div>
<Card title="useTheme 基础用法" style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 16 }}>
<p>
<strong>theme:</strong> <Tag color="blue">{theme}</Tag>
</p>
<p>
<strong>themeMode:</strong> <Tag color="green">{themeMode}</Tag>
</p>
</div>
<div style={{ marginBottom: 16 }}>
<Button
type="primary"
onClick={() => setThemeMode("dark")}
style={{ marginRight: 8 }}
>
使用深色主题
</Button>
<Button
onClick={() => setThemeMode("light")}
style={{ marginRight: 8 }}
>
使用浅色主题
</Button>
<Button
onClick={() => setThemeMode("system")}
style={{ marginRight: 8 }}
>
跟随系统
</Button>
</div>
<div
style={{
padding: "16px",
border: "1px solid #d9d9d9",
borderRadius: "6px",
backgroundColor: theme === "dark" ? "#141414" : "#ffffff",
color: theme === "dark" ? "#ffffff" : "#000000",
transition: "all 0.3s ease",
}}
>
<h3 style={{ marginBottom: 12 }}>主题预览区域</h3>
<p>
这是一个主题预览区域,展示了当前主题的样式效果。 当前主题: {theme}
,主题模式: {themeMode}
</p>
</div>
</Card>
</div>
);
};
通过合理使用这些 hooks,可以大大简化 React 应用的开发复杂度,提高代码的可维护性和用户体验。
场景类 hooks 速查表
Hook 名称 | 用途 | 描述 |
---|---|---|
useAntdTable | Antd 表格集成 | 专门为 Ant Design Table 组件设计的 hook,简化表格数据获取和分页管理 |
useFusionTable | Fusion 表格集成 | 专门为 Fusion Design Table 组件设计的 hook,提供表格数据管理功能 |
useInfiniteScroll | 无限滚动 | 实现无限滚动加载,支持触底加载更多数据,自动处理加载状态 |
usePagination | 分页管理 | 简化分页数据的获取和管理,自动处理页码和页面大小的状态 |
useDynamicList | 动态列表 | 管理动态增减的列表项,支持添加、删除、移动等操作 |
useVirtualList | 虚拟列表 | 实现大数据量的虚拟滚动列表,提升性能,减少 DOM 节点数量 |
useHistoryTravel | 历史记录 | 管理状态的历史记录,支持前进、后退、跳转到指定历史点 |
useNetwork | 网络状态 | 监听网络连接状态变化,包括在线/离线状态和网络类型 |
useSelections | 多选管理 | 管理列表的多选状态,支持全选、反选、批量操作等功能 |
useCountDown | 倒计时 | 实现倒计时功能,支持自定义格式和回调函数 |
useCounter | 计数器 | 管理数字计数器的增删改查操作,支持步长设置 |
useTextSelection | 文本选择 | 监听用户文本选择事件,获取选中的文本内容和位置信息 |
useWebSocket | WebSocket 连接 | 管理 WebSocket 连接,处理连接、断开、消息收发等操作 |
useTheme | 主题管理 | 管理应用主题状态,支持主题切换和持久化存储 |
React强大且灵活hooks库——ahooks入门实践之场景类(scene)hook详解 - 高质量源码分享平台-免费下载各类网站源码与模板及前沿动态资讯