⚠️ 【待 review】
别再用 useEffect 写请求了,试试请求策略方案
我花了三年,才发现自己在「装忙」
大概三年前,我写了一个管理后台。
客户要一个用户列表,带搜索、带分页。我心想,这还不简单?React + useEffect + fetch,三件套走起。
结果呢?代码越写越多。一个列表页,光请求逻辑就写了 60 行。还不算 loading、error、空数据的状态处理。
最后交上去,老板看了一眼:"这个页面怎么加载这么慢?"
我说:"因为每次输入搜索词都要重新请求。"
老板说:"那你不会防抖吗?"
我:"......会。"
我确实会。但每次都要手写 debounce、手动清理 effect、手动处理竞态问题------真的很累。
直到我遇到了 alova。一个让我怀疑自己过去三年是不是在「装忙」的请求库。
Before:Effect 地狱
先给你看看我之前的代码长什么样。
这是一个带搜索的用户列表页:
tsx
// 🔴 Before:80 行的 useEffect 地狱
import { useState, useEffect, useCallback } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [keyword, setKeyword] = useState('');
const [page, setPage] = useState(1);
// 获取用户列表
const fetchUsers = useCallback(async () => {
setLoading(true);
setError(null);
try {
const res = await fetch(
`/api/users?keyword=${keyword}&page=${page}&pageSize=10`
);
if (!res.ok) throw new Error('请求失败');
const data = await res.json();
setUsers(data.list);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, [keyword, page]);
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
// 防抖搜索
useEffect(() => {
const timer = setTimeout(() => {
if (keyword) fetchUsers();
}, 500);
return () => clearTimeout(timer);
}, [keyword, fetchUsers]);
// 竞态处理(手动标记)
useEffect(() => {
let cancelled = false;
const doFetch = async () => {
const res = await fetch(`/api/users?keyword=${keyword}&page=${page}`);
const data = await res.json();
if (!cancelled) setUsers(data.list);
};
doFetch();
return () => { cancelled = true; };
}, [keyword, page]);
if (loading) return <div>加载中...</div>;
if (error) return <div>出错了:{error}</div>;
return (
<div>
<input value={keyword} onChange={e => setKeyword(e.target.value)} />
<ul>
{users.map(u => <li key={u.id}>{u.name}</li>)
</ul>
</div>
);
}
这段代码的问题在哪?
- 手动状态太多 --- loading、error、data 三个状态全部自己声明、自己维护
- 防抖自己写 --- 每次都要
setTimeout+clearTimeout,一个不小心就漏了 - 竞态要自己处理 --- cancelled 标志位,写 10 次错 3 次
- 依赖地狱 ---
useCallback+useEffect的依赖链,改一个变量要排查半天
我在好几个项目里写过类似的代码。每次都告诉自己:"下次重构就好了。"
但下次,还是这样写。
After:alova 的请求策略方案
直到有人把 alova 甩到我脸上。
然后我发现......原来这些破事,早就不用自己干了。
tsx
// 🟢 After:alova 请求策略,15 行搞定
import { useRequest, useWatcher } from 'alova/client';
import { alovaInstance } from './api';
function UserList() {
const [keyword, setKeyword] = useState('');
// 列表请求 + 分页
const { loading, data: users = [], error } = useRequest(
() => alovaInstance.Get('/api/users', {
params: { keyword, page: 1, pageSize: 10 }
}),
{ initialData: [] }
);
// 搜索:监听 keyword 变化,自动带防抖
const { data: searchResult = [] } = useWatcher(
() => alovaInstance.Get('/api/users', {
params: { keyword, page: 1, pageSize: 10 }
}),
[keyword],
{
debounce: 500, // ✅ 内置防抖,一行搞定
immediate: false // 初始化不请求
}
);
if (loading) return <div>加载中...</div>;
if (error) return <div>出错了:{error.message}</div>;
return (
<div>
<input value={keyword} onChange={e => setKeyword(e.target.value)} />
<ul>
{users.map(u => <li key={u.id}>{u.name}</li>)}
</ul>
</div>
);
}
这不是魔法,这是设计。
alova 的核心理念是:把请求从「实现」变成「声明」。
| 你之前手写的 | 现在 alova 帮你做 |
|---|---|
useState 声明 loading/data/error |
useRequest 直接返回响应式状态 |
useEffect + useCallback 依赖链 |
声明性监听,依赖自动管理 |
setTimeout 手写防抖 |
debounce: 500 一行配置 |
cancelled 标志位处理竞态 |
abortLast: true 自动中断上一次 |
| 手动拼接 URL 参数 | params 对象自动处理 |
这些不是"锦上添花"的功能,而是每个前端每天都在重复踩的坑。alova 一次性帮你填平了。
什么是「请求策略」?
你可能会问:alova 到底做了什么,让代码变得这么短?
答案是 请求策略化。
传统写法里,你写的是"怎么请求"(用 fetch 还是 axios、参数怎么拼、状态怎么存)。而 alova 让你写的是"请求什么"------剩下的它自己搞定。
alova 的策略体系就是干这个的:
| 策略 Hook | 你告诉它 | 它帮你做 |
|---|---|---|
useRequest |
请求什么 | 自动管理 loading/data/error |
useWatcher |
监听什么状态 | 自动防抖、自动重新请求、自动处理竞态 |
usePagination |
分页参数 | 自动管理页码、预加载、乐观更新 |
useForm |
表单数据 | 草稿持久化、自动提交、重置 |
useAutoRequest |
轮询配置 | 自动轮询、焦点刷新、重连刷新 |
useCaptcha |
手机号 | 验证码倒计时、自动发送 |
每个 Hook 对应一个你大概率写过无数遍 的场景。不是让你少写几行,是让你直接不写。
从效果出发的反思
换了 alova 之后,我的项目发生了几个变化:
- 代码量减少 60% --- 一个页面平均从 150 行降到 60 行
- Bug 大幅减少 --- 手写 debounce 的边界条件、竞态的条件判断,全没了
- 新人上手更快 --- 不用教"你要注意竞态问题",直接说"这个用 useWatcher"
- Review 时间缩短 --- 以前 review 请求代码要一行行看 useEffect 的依赖,现在一眼扫过去就懂了
你可能会说:这不就是封装了一下吗?我自己也能封装。
对,你确实能。
但问题是------你每一次都要花时间封装,封装完了还要维护,换了项目还要重新封装。而 alova 把这些事做到了极致:开箱即用的策略、内置的缓存、服务端也能用、类型完美推断。
这不是语法糖,是范式升级。
反哺社区
这篇文章不是广告。
alova 本身是一个开源项目,社区活跃,文档齐全。它的请求策略方案也不是我吹的------你去看文档,每个策略的源码都在 GitHub 上,透明公开。
我个人从 alova 受益良多,所以想把这种思路分享给更多前端开发者。
别再用 useEffect 写请求了。你值得拥有更好的方案。
如果你也想试试:
bash
# 安装 alova
npm install alova
# 选择一个适配器
npm install alova/fetch # 或 axios/alova、uniapp-adapter 等
然后就可以写下你第一个策略化请求了。
本文使用 alova v3。查看官方文档:alova.js.org