Files
TERES_web_frontend/docs/ahooks-usage-guide.md
guangfei.zhao d24b371929 feat(knowledge): add pipeline support and build mode selection
refactor(configuration): reorganize naive config form with pipeline selector
feat(form): add RadioFormField component for build mode selection
docs: add ahooks usage guide for common patterns
style: update app title and favicon
chore: clean up unused agent interfaces
2025-11-06 23:06:23 +08:00

287 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Ahooks 使用指南(基础、进阶与常见用法)
Ahooks 是一套高质量、可复用的 React Hooks 集合,适合在业务工程中快速构建稳定的交互与数据逻辑。本文档覆盖快速上手、基础用法、`useRequest` 核心与进阶、常见场景模板、最佳实践与坑位排查结合本项目技术栈React 18 + Vite + MUI
目录
- 为什么选 Ahooks
- 安装与类型支持
- 快速上手:页面标题(`useTitle`
- 基础能力:状态/事件/定时/存储/性能
- useRequest 基础与进阶(自动/手动、轮询、缓存、SWR、重试等
- 常见场景模板(搜索防抖、任务轮询、分页、依赖请求、自动保存)
- 最佳实践与常见坑位
- 速查表与参考资料
## 为什么选用 Ahooks
- 大量生产验证的 Hooks覆盖状态管理、请求、DOM、浏览器、性能优化等场景。
- API 统一、可组合,适合封装成业务级自定义 Hook。
- 降低维护成本:内置防抖/节流、轮询、缓存、并发控制、卸载安全、SWR 等复杂能力。
## 安装与类型支持
```bash
pnpm add ahooks
```
TypeScript 用户无需额外配置Ahooks 提供完整的类型提示。
## 快速上手更新页面标题useTitle
最直接的需求就是“更新页面标题”。`useTitle` 在组件挂载时设置 `document.title`,卸载时可恢复。
```tsx
import { useTitle } from 'ahooks';
export default function KnowledgeListPage() {
useTitle('TERES · 知识库列表', { restoreOnUnmount: true });
return (<div>...</div>);
}
```
如需统一加前后缀,可封装:
```tsx
import { useTitle } from 'ahooks';
export function usePageTitle(title?: string, opts?: { prefix?: string; suffix?: string }) {
const final = [opts?.prefix, title, opts?.suffix].filter(Boolean).join(' · ');
useTitle(final || 'TERES', { restoreOnUnmount: true });
}
```
## 基础能力(常用 Hooks
- useBoolean / useToggle布尔与枚举状态切换
```tsx
const [visible, { setTrue, setFalse, toggle }] = useBoolean(false);
```
- useDebounceFn / useThrottleFn防抖与节流输入、滚动、窗口变化
```tsx
const { run: onSearchChange, cancel } = useDebounceFn((kw: string) => setKeywords(kw), { wait: 300 });
```
- useEventListener声明式事件监听自动清理
```tsx
useEventListener('resize', () => console.log(window.innerWidth), { target: window });
```
- useLocalStorageState / useSessionStorageState状态持久化
```tsx
const [lang, setLang] = useLocalStorageState('lang', { defaultValue: 'zh' });
```
- useInterval / useTimeout定时任务自动清理
```tsx
useInterval(() => refresh(), 30000);
```
- useMemoizedFn稳定函数引用避免子组件无谓重渲染/解绑
```tsx
const onNodeClick = useMemoizedFn((id: string) => openDetail(id));
```
- useLatest防陈旧闭包异步/周期函数读取最新数据
```tsx
const latestState = useLatest(state);
useInterval(() => { doSomething(latestState.current); }, 1000);
```
- useSafeState / useUnmount卸载安全避免内存泄漏或报错
```tsx
const [data, setData] = useSafeState<any>(null);
useUnmount(() => cancelRequest());
```
## useRequest核心与进阶
`useRequest` 是 Ahooks 的异步数据管理核心,内置自动/手动触发、轮询、防抖/节流、屏幕聚焦重新请求、错误重试、loading 延迟、SWRstale-while-revalidate缓存等能力。[0][1]
### 基础用法(自动请求)
```tsx
import { useRequest } from 'ahooks';
import agentService from '@/services/agent_service';
export default function AgentList() {
const { data, loading, error, refresh } = useRequest(() => agentService.fetchAgentList());
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {String(error)}</div>;
return <div>{JSON.stringify(data)}</div>;
}
```
### 手动触发与参数管理
```tsx
const { run, runAsync, loading, params } = useRequest((kw: string) => agentService.searchAgents({ keywords: kw }), {
manual: true,
onSuccess: (data, [kw]) => console.log('searched', kw, data),
});
// 触发
run('rag');
// 或 await runAsync('rag').catch(console.error);
```
### 生命周期回调
```tsx
useRequest(api, {
onBefore: (p) => console.log('before', p),
onSuccess: (d) => console.log('success', d),
onError: (e) => console.error('error', e),
onFinally: (p, d, e) => console.log('finally'),
});
```
### 刷新上一次请求refresh与数据变更mutate
```tsx
const { run, refresh, mutate, data } = useRequest(() => api.getUser(1), { manual: true });
// 修改页面数据,不等待接口返回
mutate((old) => ({ ...old, name: 'New Name' }));
// 使用上一次参数重新发起请求
refresh();
```
### 取消响应cancel与竞态取消
```tsx
const { run, cancel } = useRequest(api.longTask, { manual: true });
run();
// 某些场景需要忽略本次 promise 的响应
cancel();
```
### 轮询与停止条件
```tsx
const { data, cancel } = useRequest(() => agentService.getJobStatus(), {
pollingInterval: 5000,
pollingWhenHidden: false,
onSuccess: (res) => { if (res.done) cancel(); },
});
```
### 防抖 / 节流 / 重试 / 延迟 / 焦点刷新
```tsx
useRequest(searchService.query, {
manual: true,
debounceWait: 300,
throttleWait: 1000,
retryCount: 2,
loadingDelay: 200,
refreshOnWindowFocus: true,
});
```
### 缓存cacheKey与过期时间staleTime
```tsx
const { data } = useRequest(() => agentService.fetchAgentList(), {
cacheKey: 'agent:list',
staleTime: 60_000, // 1分钟内复用缓存SWR 策略)
});
```
### 条件就绪ready与依赖刷新refreshDeps
```tsx
useRequest(() => api.getById(id), {
ready: !!id,
refreshDeps: [id],
});
```
### 默认参数defaultParams
```tsx
const { data } = useRequest((id: number) => api.getById(id), {
defaultParams: [1],
});
```
## 常见场景模板
### 搜索输入防抖
```tsx
const { run: search } = useRequest((kw: string) => api.search({ keywords: kw }), { manual: true });
const { run: onChange, cancel } = useDebounceFn((kw: string) => search(kw), { wait: 300 });
```
### 任务状态轮询,完成后停止
```tsx
const { data, cancel } = useRequest(() => api.getJobStatus(jobId), {
pollingInterval: 5000,
pollingWhenHidden: false,
onSuccess: (res) => { if (res.done) cancel(); },
});
```
### 分页列表(前端分页)
```tsx
const { data, loading } = useRequest(() => api.list({ page, pageSize, keywords }), {
refreshDeps: [page, pageSize, keywords],
});
```
### 依赖请求(根据上游结果触发下游)
```tsx
const userReq = useRequest(() => api.getUser(userId), { ready: !!userId });
const postsReq = useRequest(() => api.getPosts(userReq.data?.id), {
ready: !!userReq.data?.id,
});
```
### 自动保存(组件生命周期安全)
```tsx
useInterval(async () => {
if (!graphValid) return;
await api.setAgentDSL(payload);
}, 30_000);
```
## 最佳实践与常见坑位
- 顶层调用 Hooks避免条件语句中调用保持顺序一致。
- 依赖项变动可能导致频繁触发;`useMemoizedFn` 可保持函数引用稳定。
- 轮询需显式停止;结合 `cancel()` 与 `pollingWhenHidden=false`。
- 缓存需设置合理 `staleTime`避免过时数据SWR 适合只读列表数据。
- 组件卸载时自动忽略响应,避免卸载后 setState。
- SSR 环境下注意 `window/document` 可用性,必要时使用 `useIsomorphicLayoutEffect`。
## 与本项目的结合建议
- 知识库列表搜索:将 `setTimeout` 防抖替换为 `useDebounceFn`,代码更简洁且可取消。
- 任务状态轮询:用 `useRequest` 的 `pollingInterval + cancel()` 替代手写 `setInterval`。
- 自动保存:使用 `useInterval`,确保随组件生命周期清理(我们已修复泄漏问题)。
- 事件绑定:用 `useEventListener` 管理 `window`/`document` 事件,自动清理。
- 页面标题:用 `useTitle` 或统一的 `usePageTitle` 封装。
## 速查表(精选)
- 状态:`useBoolean`、`useToggle`、`useSetState`、`useControllableValue`
- 请求:`useRequest`
- DOM/事件:`useEventListener`、`useInViewport`、`useClickAway`
- 定时:`useInterval`、`useTimeout`
- 存储:`useLocalStorageState`、`useSessionStorageState`
- 性能/安全:`useDebounceFn`、`useThrottleFn`、`useMemoizedFn`、`useLatest`、`useSafeState`
## 参考资料
- 官方文档useRequest 快速上手)[0] https://ahooks.js.org/zh-CN/hooks/use-request/index
- 官方文档useRequest 基础用法)[1] https://ahooks.js.org/zh-CN/hooks/use-request/basic
- 项目仓库https://github.com/alibaba/hooks
---
实战建议:如果只需要“更新 web 标题”,直接在页面组件里使用 `useTitle`;若需统一标题规范或根据路由动态拼接,封装一个 `usePageTitle` 更可维护。`useRequest` 作为核心请求管理,优先在网络交互中使用它的自动/手动、轮询、缓存与重试能力,替代手写的定时器与状态机。