Skip to content

ahooks useRequest 踩坑文档:第一个参数类型导致执行次数异常

一、问题现象

在使用 ahooks 的 useRequest 时,发现一个奇怪的现象:当第一个参数传入函数时,请求只会执行一次(符合预期的缓存或依赖触发逻辑);但如果传入Promise,请求会不受控制地多次执行,甚至陷入循环执行的情况。

二、原因解析

要理解这个问题,需从 useRequest 的设计逻辑和参数预期入手:

1. useRequest 对第一个参数的预期

useRequest 的第一个参数是请求函数(Function),该函数需要返回一个 Promise(用于发起异步请求)。useRequest 会根据依赖项、缓存策略等来控制这个函数的执行时机。

示例(正确用法):

jsx
const { data, run } = useRequest(() => {
  return axios.get('/api/data');
}, {
  // 配置项
});

2. 传入 Promise 时的执行逻辑

如果直接传入一个 Promise 而非函数,useRequest 的执行逻辑会被完全破坏:

  • useRequest 在初始化时会立即执行这个 Promise(因为它不是函数,无法被 useRequest 按逻辑延迟执行)。
  • 当 Promise 执行后,若触发了组件重渲染(比如数据更新导致组件重新渲染),useRequest 会再次执行这个 Promise(因为它是一个“值”而非“可复用的函数”),从而导致多次执行,甚至循环执行。

示例(错误用法,会导致多次执行):

jsx
// 错误:直接传入 Promise
const promise = axios.get('/api/data');
const { data, run } = useRequest(promise);

三、踩坑场景还原

以下场景会复现该问题:

jsx
import { useRequest } from 'ahooks';
import axios from 'axios';

function MyComponent() {
  // 错误:第一个参数是 Promise 而非函数
  const { data, loading } = useRequest(axios.get('/api/user'));

  if (loading) return <div>加载中...</div>;
  return <div>用户数据:{data.name}</div>;
}

在这个例子中,axios.get('/api/user') 会在组件初始化时立即执行,当请求完成后组件重渲染,useRequest 又会再次执行这个 Promise,导致请求不断重复执行。

四、解决方案

严格遵循 useRequest 的设计规范,第一个参数传入返回 Promise 的函数

jsx
import { useRequest } from 'ahooks';
import axios from 'axios';

function MyComponent() {
  // 正确:传入返回 Promise 的函数
  const { data, loading } = useRequest(() => {
    return axios.get('/api/user');
  });

  if (loading) return <div>加载中...</div>;
  return <div>用户数据:{data.name}</div>;
}

这样 useRequest 就能根据其内部的依赖管理、缓存策略等逻辑,精准控制请求的执行时机,避免多次无意义执行。

五、总结

  • useRequest 第一个参数必须是返回 Promise 的函数,而非直接的 Promise。
  • 直接传入 Promise 会导致请求在组件重渲染时反复执行,破坏 useRequest 的正常逻辑。
  • 遵循正确的参数类型传入方式,才能让 useRequest 发挥其缓存、防抖、节流等强大能力,保障请求的稳定性和性能。