使用 ahooks - useRequest 轻松实现乐观更新
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
这是我关于 ahooks - useRequest 系列文章的第二篇,其他两篇请查看:
在上一篇文章:使用 ahooks 中的 useRequest 轻松管理React中的网络请求 ,我们介绍了 useRequest
这个 Hook 的使用以及配置。
本文我们主要介绍如何通过 useRequest
实现乐观更新。
什么是乐观更新
乐观更新 Optimistic Updates,即不等待接口返回数据,在发起请求时就对数据进行修改,提前将交互结果显示在用户界面上。
一般的应用乐观更新的场景,是对数据源的可预测修改,即请求发出后我们可以知道成功 或者 失败 会对源数据产生什么影响。
举个栗子,假如我们要对用户信息进行修改一般涉及到如下两个接口:
[get] api/user/id
获取到用户信息[put] api/user/id
修改用户信息
一般来说,接口2
在提交成功后只会返回成功或者失败的信息,那么我们通常是在接口2
调用成功后,再次请求接口1
更新页面。
就像我前面说的这个概念这是一个非常典型的可预测修改,我们其实完全没有必要再次请求接口1,完全可以使用我们自己已有的数据来修改原来的数据源,从而减少网络请求次数、提高用户界面响应速度。
普通:接口1
-> 接口2
-> 接口1
,一发送3个请求需要等待时间较长,用户界面响应取决于网络响应
乐观更新:接口1
-> 接口2
(本地修改数据源),只发送两次请求,只等待一次接口1
的响应
简单来说乐观更新的概念,就是我们乐观的认为这个修改请求会成功,并且我们可以预测成功或失败后数据源如何变化,在真实数据(服务端状态)变化之前,修改用户本地数据刷新用户界面。
如何实现
这里就要请出我们在上一篇文章介绍的 mutate
函数与生命周期回调,用我们上面修改用户信息的例子写一个简单的demo,我会分步骤来介绍每一个步骤的意义。
接口模拟
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// 模拟请求用户信息
const fetchUserInfo = (id) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
name: `John`,
id,
time: new Date().toLocaleString()
});
}, 500);
})
}
// 模拟修改用户信息接口请求成功 or 失败
const changeUserName = (name) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve();
} else {
reject(new Error('Failed to modify username'));
}
}, 1000);
});
}在组件中使用 useRequest
1
2
3
4
5
6
7
8
9
10
11
12
13// 接口1 获取用户信息接口
const { data, loading, error, run, refresh, mutate } = useRequest(() => fetchUserInfo(id));
// 使用 usePrevious 保存上一次的数据
const originData = usePrevious(data);
// 接口2 修改用户接口
const { run: updateName } = useRequest(changeUserName, {
manual: true,
onError: () => {
// 乐观更新失败使用原始数据恢复
mutate(originData);
alert('操作失败!');
}
})这里我们额外引入了 ahooks 中的一个 钩子函数:usePrevious,它可以帮我们保存状态变更前的值,这样我们可以方便的通过它来回溯数据,这个钩子函数我们以后有机会会专门介绍一下。
这里要注意,
mutate
函数是来自于数据源接口,用来操作数据源状态。用于引起突变的实际是接口2,我们将它设置为手动触发,并且配置生命周期函数。当请求成功时说明对服务端状态修改成功,一般来说不需要额外操作。失败时,说明乐观更新失败,我们需要用原始数据来恢复用户界面并对用户进行提示。处理突变
1
2
3
4
5
6
7
8
9
10
11const handleChange = () => {
if (name) {
// 发起修改请求
updateName(name);
// 发起请求时通过mutate函数乐观更新原始数据
mutate(state => ({
...state,
name
}))
}
}处理突变这里很好理解,就是引起突变的请求发出后,我们就应该立即使用
mutate
函数,改变原始数据,进行乐观更新。
题外话
在 ahooks 中实现乐观更新的操作步骤上虽然有一点繁琐,但是胜在逻辑很容易理解。比较起 react-query 来说,useRequest
需要多些一些代码,例如需要自己保存修改之前的原始数据(用于突变失败后的数据恢复)。
一般来说我们在大多数情况都不是必须使用乐观更新,他不是一个必选项,只有在频繁涉及到这种可预测修改时,可以使用乐观更新来优化用户体验。
乐观更新的限定一定是可预测修改,如果一个操作之后对用户界面的影响是未知的,那么我们只能按照原来的方式处理。
个人感官上,useRequest 可以作为接触 服务端状态管理 的入门,如果我们要深入的学习理解 服务端状态管理 ,还是需要去学习 诸如 react-query 或者 rtk-query 的。
上文演示的乐观更新其实并不适合应用于生产,这更多是一个入门的demo,我会在下一篇文章介绍 swr、数据缓存、数据共享时,改造这段代码,让他更符合真实应用时的使用方法,敬请期待!