ahooks useRequest 深度解析:一个 Hook 搞定所有请求

二、核心功能详解

1. 自动管理请求状态

javascript 复制代码
const { data, loading, error, run, refresh, cancel } = useRequest(
  fetchUserList,
  {
    manual: false,  // 自动执行
    defaultParams: [{ page: 1 }],  // 默认参数
  }
);

// run: 手动触发
// refresh: 使用上次参数重新请求
// cancel: 取消当前请求

2. 防抖与节流

javascript 复制代码
// 搜索场景:防抖
const { data, loading } = useRequest(searchAPI, {
  debounceWait: 300,  // 300ms 防抖
  manual: true,
});

// 滚动加载:节流
const { run } = useRequest(loadMore, {
  throttleWait: 1000,  // 1s 节流
  manual: true,
});

3. 轮询

javascript 复制代码
// 每 3 秒轮询一次
const { data } = useRequest(getStatus, {
  pollingInterval: 3000,
  pollingWhenHidden: false,  // 页面隐藏时停止轮询
});

// 条件轮询
const { data } = useRequest(getJobStatus, {
  pollingInterval: 2000,
  pollingErrorRetryCount: 3,  // 错误重试次数
  onSuccess: (result) => {
    if (result.status === 'completed') {
      // 完成后停止轮询
      return false;
    }
  }
});

4. 依赖刷新

javascript 复制代码
const [userId, setUserId] = useState('1');

const { data } = useRequest(
  () => fetchUser(userId),
  {
    refreshDeps: [userId],  // userId 变化时自动重新请求
  }
);

5. 缓存机制

javascript 复制代码
// SWR 模式:先返回缓存,后台更新
const { data, loading } = useRequest(fetchUser, {
  cacheKey: 'user-data',
  staleTime: 5000,  // 5s 内认为数据新鲜
  cacheTime: 300000,  // 缓存保留 5 分钟
});

// 清除缓存
import { clearCache } from 'ahooks';
clearCache('user-data');

6. 错误重试

javascript 复制代码
const { data, error, retry } = useRequest(unstableAPI, {
  retryCount: 3,  // 失败后重试 3 次
  retryInterval: 1000,  // 重试间隔 1s
  onError: (error, params) => {
    console.log('请求失败', error);
  }
});

三、进阶场景

并行请求

javascript 复制代码
const user = useRequest(fetchUser);
const posts = useRequest(fetchPosts);
const comments = useRequest(fetchComments);

const loading = user.loading || posts.loading || comments.loading;

串行请求

javascript 复制代码
const { data: user } = useRequest(fetchUser);

const { data: posts } = useRequest(
  () => fetchUserPosts(user.id),
  {
    ready: !!user,  // user 存在时才执行
    refreshDeps: [user],
  }
);

分页加载

javascript 复制代码
function UserList() {
  const { data, loading, loadMore, loadingMore, noMore } = useRequest(
    (d) => fetchList({ page: d?.nextPage || 1 }),
    {
      loadMore: true,
      isNoMore: (d) => !d?.hasMore,
    }
  );
  
  return (
    <>
      {data?.list.map(item => <Item key={item.id} {...item} />)}
      {!noMore && (
        <Button onClick={loadMore} loading={loadingMore}>
          加载更多
        </Button>
      )}
    </>
  );
}

乐观更新

javascript 复制代码
const { run: deleteItem } = useRequest(deleteAPI, {
  manual: true,
  onBefore: (params) => {
    // 立即更新 UI
    setList(list => list.filter(item => item.id !== params[0]));
  },
  onError: (error, params) => {
    // 失败时回滚
    message.error('删除失败');
    refresh();
  }
});

四、与其他方案对比

特性 useRequest React Query SWR
学习成本
功能完整度 很高
包体积 较大
防抖节流 内置 需自己实现 需自己实现
轮询 内置 内置 需配置
TypeScript 良好 优秀 良好

五、最佳实践

  1. 合理使用缓存:列表、详情等读多写少的数据适合缓存
  2. 设置合适的防抖时间:搜索建议 300-500ms
  3. 避免过度轮询:根据业务需求设置合理的轮询间隔
  4. 善用 ready 参数:避免无效请求
  5. 统一错误处理:在全局配置中处理通用错误
javascript 复制代码
// 全局配置
import { configResponsive } from 'ahooks';

configResponsive({
  onError: (error) => {
    if (error.code === 401) {
      // 统一处理未登录
      redirectToLogin();
    }
  }
});

六、源码解析(简化版)

useRequest 的核心实现思路:

javascript 复制代码
function useRequest(service, options) {
  const [state, setState] = useState({
    data: undefined,
    loading: false,
    error: undefined,
  });
  
  const run = useCallback(async (...params) => {
    setState(s => ({ ...s, loading: true }));
    
    try {
      const data = await service(...params);
      setState({ data, loading: false, error: undefined });
    } catch (error) {
      setState(s => ({ ...s, loading: false, error }));
    }
  }, [service]);
  
  useEffect(() => {
    if (!options.manual) {
      run(...(options.defaultParams || []));
    }
  }, []);
  
  return { ...state, run };
}

实际实现还包括:

  • 防抖节流的 debounce/throttle 包装
  • 轮询的 setInterval 管理
  • 缓存的 Map 存储
  • 依赖追踪的 useEffect
  • 请求取消的 AbortController

总结

useRequest 是一个功能强大且易用的请求管理 Hook,它封装了日常开发中 90% 的请求场景。通过合理使用其提供的能力,可以大幅减少样板代码,提升开发效率。

推荐在中小型项目中直接使用 useRequest,大型项目可以考虑 React Query 获得更强的数据管理能力。

如果这篇文章对你有帮助,欢迎点赞收藏!

相关推荐
兆子龙2 小时前
React Suspense 从入门到实战:让异步加载更优雅
java·javascript
KKKK2 小时前
SSE(Server-Sent Events)流式传输原理和XStream实践
前端·javascript
子兮曰3 小时前
Humanizer-zh 实战:把 AI 初稿改成“能发布”的技术文章
前端·javascript·后端
Din3 小时前
主动取消的防抖
前端·javascript·typescript
H5开发新纪元4 小时前
Nginx 部署 Vue3 项目完整指南
前端·javascript·面试
决斗小饼干4 小时前
跨语言移植手记:把 TypeScript 的 Codex SDK 请进 .NET 世界
前端·javascript·typescript
进击的尘埃4 小时前
Vitest 浏览器模式:别再用 jsdom 骗自己了
javascript
bluceli4 小时前
JavaScript模块化深度解析:从CommonJS到ES Modules的演进之路
前端·javascript
前端人类学4 小时前
前端输入框禁用:disabled、readonly 与.prop (‘disabled‘, true) 完全解析
前端·javascript