数据请求
数据请求
数据请求库的特性
假设有个最简单的需求,
- 请求的方法写在哪里?请求来的数据放在哪里?
- 如果请求还未完成,组件已被
Unmount ,有没有做特殊处理? - 请求的参数来自哪里,参数变化会触发重新请求吗?调用写在哪里?
- 请求对应的几个状态
: loading ,refreshing,error 在组件上处理了么? - 如果请求需要轮询,怎么做?有没有处理页面
visibilitychange 的情况? - 如果需要在参数变化后重新请求,如果参数频繁更新,会出现竞态(旧的请求因为慢,晚于后发的请求
resolve )的问题吗?
这里我们以
基础网络请求
function getUsername() {
return Promise.resolve("jack");
}
export default () => {
const { data, error, loading } = useRequest(getUsername);
if (error) return <div>failed to load</div>;
if (loading) return <div>loading...</div>;
return <div>Username: {data}</div>;
};
这是一个最简单的网络请求示例。在这个例子中
手动请求
对于“写”请求,我们一般需要手动触发,比如添加用户,编辑信息,删除用户等等。
export default () => {
const { run, loading } = useRequest(changeUsername, { manual: true });
return (
<Button onClick={() => run("new name")} loading={loading}>
Edit
</Button>
);
};
轮询
对于需要保持新鲜度的数据,我们通常需要不断发起网络请求以更新数据。
export default () => {
const { data } = useRequest(getUsername, { pollingInterval: 1000 });
return <div>Username: {data}</div>;
};
同时通过设置
并行请求
同一个接口,我们需要维护多个请求状态。示例中的并行请求有几个特点:
- 删除
n 个不同的用户,则需要维护n 个请求状态。 - 多次删除同一个用户,则只需要维护最后一个请求。
export default () => {
const { run, fetches } = useRequest(deleteUser, {
manual: true,
fetchKey: (id) => id, // 不同的 ID,分类不同
});
return (
<div>
<Button
loading={fetches.A?.loading}
onClick={() => {
run("A");
}}
>
删除 1
</Button>
<Button
loading={fetches.B?.loading}
onClick={() => {
run("B");
}}
>
删除 2
</Button>
<Button
loading={fetches.C?.loading}
onClick={() => {
run("C");
}}
>
删除 3
</Button>
</div>
);
};
防抖& 节流
通常在边输入边搜索的场景中,我们会用到防抖功能,以节省不必要的网络请求。通过
export default () => {
const { data, loading, run, cancel } = useRequest(getEmail, {
debounceInterval: 500,
manual: true,
});
return (
<div>
<Select onSearch={run} loading={loading}>
{data &&
data.map((i) => (
<Option key={i} value={i}>
{i}
</Option>
))}
</Select>
</div>
);
};
缓存& SWR & 预加载
对于一些数据不是经常变化的接口,使用
const { data, loading } = useRequest(getArticle, {
cacheKey: "articleKey",
});
同时需要注意,同一个
屏幕聚焦重新请求
通过配置
集成请求库
考虑到使用便捷性,
// 用法 1
const { data, error, loading } = useRequest("/api/userInfo");
// 用法 2
const { data, error, loading } = useRequest({
url: "/api/changeUsername",
method: "post",
});
// 用法 3
const { data, error, loading, run } = useRequest(
(userId) => `/api/userInfo/${userId}`
);
// 用法 4
const { loading, run } = useRequest((username) => ({
url: "/api/changeUsername",
method: "post",
data: { username },
}));
分页
中台应用中最多的就是表格和表单了。对于一个表格,我们要处理非常多的请求逻辑,包括不限于:
- page、pageSize、
total 管理 - 筛选条件变化,重置分页,重新发起网络请求
export default () => {
const [gender, setGender] = useState("male");
const { tableProps } = useRequest(
(params) => {
return getTableData({ ...params, gender });
},
{
paginated: true,
refreshDeps: [gender],
}
);
const columns = [];
return <Table columns={columns} rowKey="email" {...tableProps} />;
};
加载更多
加载更多的场景也是日常开发中常见的需求。在加载场景中,我们一般需要处理:
- 分页
offset 、pageSize 等管理 - 首次加载,加载更多状态管理
- 上拉自动加载更多
- 组件第二次加载时,希望能记录之前的数据,并滚动到之前的位置
const { data, loading, loadMore, loadingMore } = useRequest(
(d) => getLoadMoreList(d?.nextId, 3),
{
loadMore: true,
cacheKey: "loadMoreDemoCacheId",
fetchKey: (d) => `${d?.nextId}-`,
}
);