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

8.9 KiB
Raw Permalink Blame History

Ahooks 使用指南(基础、进阶与常见用法)

Ahooks 是一套高质量、可复用的 React Hooks 集合,适合在业务工程中快速构建稳定的交互与数据逻辑。本文档覆盖快速上手、基础用法、useRequest 核心与进阶、常见场景模板、最佳实践与坑位排查结合本项目技术栈React 18 + Vite + MUI

目录

  • 为什么选 Ahooks
  • 安装与类型支持
  • 快速上手:页面标题(useTitle
  • 基础能力:状态/事件/定时/存储/性能
  • useRequest 基础与进阶(自动/手动、轮询、缓存、SWR、重试等
  • 常见场景模板(搜索防抖、任务轮询、分页、依赖请求、自动保存)
  • 最佳实践与常见坑位
  • 速查表与参考资料

为什么选用 Ahooks

  • 大量生产验证的 Hooks覆盖状态管理、请求、DOM、浏览器、性能优化等场景。
  • API 统一、可组合,适合封装成业务级自定义 Hook。
  • 降低维护成本:内置防抖/节流、轮询、缓存、并发控制、卸载安全、SWR 等复杂能力。

安装与类型支持

pnpm add ahooks

TypeScript 用户无需额外配置Ahooks 提供完整的类型提示。

快速上手更新页面标题useTitle

最直接的需求就是“更新页面标题”。useTitle 在组件挂载时设置 document.title,卸载时可恢复。

import { useTitle } from 'ahooks';

export default function KnowledgeListPage() {
  useTitle('TERES · 知识库列表', { restoreOnUnmount: true });
  return (<div>...</div>);
}

如需统一加前后缀,可封装:

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布尔与枚举状态切换

    const [visible, { setTrue, setFalse, toggle }] = useBoolean(false);
    
  • useDebounceFn / useThrottleFn防抖与节流输入、滚动、窗口变化

    const { run: onSearchChange, cancel } = useDebounceFn((kw: string) => setKeywords(kw), { wait: 300 });
    
  • useEventListener声明式事件监听自动清理

    useEventListener('resize', () => console.log(window.innerWidth), { target: window });
    
  • useLocalStorageState / useSessionStorageState状态持久化

    const [lang, setLang] = useLocalStorageState('lang', { defaultValue: 'zh' });
    
  • useInterval / useTimeout定时任务自动清理

    useInterval(() => refresh(), 30000);
    
  • useMemoizedFn稳定函数引用避免子组件无谓重渲染/解绑

    const onNodeClick = useMemoizedFn((id: string) => openDetail(id));
    
  • useLatest防陈旧闭包异步/周期函数读取最新数据

    const latestState = useLatest(state);
    useInterval(() => { doSomething(latestState.current); }, 1000);
    
  • useSafeState / useUnmount卸载安全避免内存泄漏或报错

    const [data, setData] = useSafeState<any>(null);
    useUnmount(() => cancelRequest());
    

useRequest核心与进阶

useRequest 是 Ahooks 的异步数据管理核心,内置自动/手动触发、轮询、防抖/节流、屏幕聚焦重新请求、错误重试、loading 延迟、SWRstale-while-revalidate缓存等能力。[0][1]

基础用法(自动请求)

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>;
}

手动触发与参数管理

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);

生命周期回调

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

const { run, refresh, mutate, data } = useRequest(() => api.getUser(1), { manual: true });

// 修改页面数据,不等待接口返回
mutate((old) => ({ ...old, name: 'New Name' }));
// 使用上一次参数重新发起请求
refresh();

取消响应cancel与竞态取消

const { run, cancel } = useRequest(api.longTask, { manual: true });
run();
// 某些场景需要忽略本次 promise 的响应
cancel();

轮询与停止条件

const { data, cancel } = useRequest(() => agentService.getJobStatus(), {
  pollingInterval: 5000,
  pollingWhenHidden: false,
  onSuccess: (res) => { if (res.done) cancel(); },
});

防抖 / 节流 / 重试 / 延迟 / 焦点刷新

useRequest(searchService.query, {
  manual: true,
  debounceWait: 300,
  throttleWait: 1000,
  retryCount: 2,
  loadingDelay: 200,
  refreshOnWindowFocus: true,
});

缓存cacheKey与过期时间staleTime

const { data } = useRequest(() => agentService.fetchAgentList(), {
  cacheKey: 'agent:list',
  staleTime: 60_000, // 1分钟内复用缓存SWR 策略)
});

条件就绪ready与依赖刷新refreshDeps

useRequest(() => api.getById(id), {
  ready: !!id,
  refreshDeps: [id],
});

默认参数defaultParams

const { data } = useRequest((id: number) => api.getById(id), {
  defaultParams: [1],
});

常见场景模板

搜索输入防抖

const { run: search } = useRequest((kw: string) => api.search({ keywords: kw }), { manual: true });
const { run: onChange, cancel } = useDebounceFn((kw: string) => search(kw), { wait: 300 });

任务状态轮询,完成后停止

const { data, cancel } = useRequest(() => api.getJobStatus(jobId), {
  pollingInterval: 5000,
  pollingWhenHidden: false,
  onSuccess: (res) => { if (res.done) cancel(); },
});

分页列表(前端分页)

const { data, loading } = useRequest(() => api.list({ page, pageSize, keywords }), {
  refreshDeps: [page, pageSize, keywords],
});

依赖请求(根据上游结果触发下游)

const userReq = useRequest(() => api.getUser(userId), { ready: !!userId });
const postsReq = useRequest(() => api.getPosts(userReq.data?.id), {
  ready: !!userReq.data?.id,
});

自动保存(组件生命周期安全)

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,代码更简洁且可取消。
  • 任务状态轮询:用 useRequestpollingInterval + cancel() 替代手写 setInterval
  • 自动保存:使用 useInterval,确保随组件生命周期清理(我们已修复泄漏问题)。
  • 事件绑定:用 useEventListener 管理 window/document 事件,自动清理。
  • 页面标题:用 useTitle 或统一的 usePageTitle 封装。

速查表(精选)

  • 状态:useBooleanuseToggleuseSetStateuseControllableValue
  • 请求:useRequest
  • DOM/事件:useEventListeneruseInViewportuseClickAway
  • 定时:useIntervaluseTimeout
  • 存储:useLocalStorageStateuseSessionStorageState
  • 性能/安全:useDebounceFnuseThrottleFnuseMemoizedFnuseLatestuseSafeState

参考资料


实战建议:如果只需要“更新 web 标题”,直接在页面组件里使用 useTitle;若需统一标题规范或根据路由动态拼接,封装一个 usePageTitle 更可维护。useRequest 作为核心请求管理,优先在网络交互中使用它的自动/手动、轮询、缓存与重试能力,替代手写的定时器与状态机。