该系列其他文章可以点击查看专栏👉🏻👉🏻react-query手把手教程系列
介绍
在很多情况下,你没有必要为用户展示loading动画,因为你可以使用当前的一些缓存数据来默认填充UI的显示界面。毕竟很多人非常讨厌菊花动画,转的人心烦意乱(笔者就是其中之一)。这也是提升用户体验的一种办法。
本篇文章将会向你介绍,应该如何使用placeholderData
以及initialData
配置项。
实践
Placeholder Data
其实这个标题可以翻译为占位数据,但是一旦换成占位数据,大家的观感就没有那么强。说起placeholder
,大家一定就会想起各种三方UI库中,输入组件的placeholder
属性。比如下面的提示Enter your username
:
placeholder
的数据仅仅是一个非常不重要的内容,一旦用户输入了数据,这个placeholder就会被立即替代。
有了比照之后,现在就来看看react-query中的placeholderData
配置项。
const issuesQuery = useQuery(
["issues", repo, org],
fetchIssues,
{
// ①
placeholderData: [],
}
)
①:在还未第一次请求到数据之前的这段时间,如果获取issuesQuery.data
的值,此时应该为[]
,一旦issuesQuery
查询从后端获取到了数据,此时issuesQuery.data
的值将会被替换为实际的后端值。
其实placeholderData
类似于解构时;如果解构项的值为undefined
,将会采用开发者设置的默认值,比如下面的例子:
const { data = [] } = useQuery(
["issues", repo, org],
fetchIssues,
)
如果解构时data
为undefined
,将会默认赋值[]
,与上面的效果一致。
当你需要为用户提供假数据时,就需要使用placeholderData
。
比如在获取用户数据时,为了UI不那么难看,可以先默认展示一个占位头像,这里默认展示掘金酱的头像:
const UserAvatar = () => {
const userQuery = useQuery(
["user"],
fetchUser,
{
placeholderData: {
avatar_url: "https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/mirror-assets/168e0858b6ccfd57fe5~tplv-t2oaga2asx-no-mark:180:180:180:180.awebp",
}
}
)
return (
<img src={userQuery.data.avatar_url} alt="Avatar" />
)
}
但是请注意,之前有介绍过依赖查询,假如某个依赖查询,依赖了该数据进行判断,就会出现问题。此时需要使用isPlaceholderData
属性来判断当前数据是否真实:
如果有遗忘了依赖查询的同学,请点击这里补课👉🏻react-query手把手教程③-并行请求及依赖请求)
const userQuery = useQuery(
["user"],
fetchUser,
)
const userIssues = useQuery(
["userIssues", userQuery.data.login],
fetchUserIssues,
{
enabled: !userQuery.isPlaceholderData
&& userQuery.data?.login,
}
)
在上面的例子中,如果当前用户数据是一个PlaceholderData
数据,那么就不去执行userIssues
查询。
请注意!!
placeholderData
并不会存储在缓存中,因此它只是用于处理虚假数据使用!!如果你希望使用真实数据进行预加载并将该数据放入缓存中,请使用下面介绍的initialData
。
Initial Data
与placeholderData
相比initialData
就属于真实数据的一种。
比如在页面刚刚加载时,你有一些硬编码的数据,需要在前端展示,此时就需要将这个数据灌入initialData
中,下面的代码中,系统会硬编码一位管理员,并将其作为初始化的数据传入,此时数据虽然不是从后端请求的,但是一样与后端的数据类似,不是占位数据,因此需要在initialData
中传入:
const hardCodedAdminUsers = [
{
id: 1,
login: "orange",
name: "修仙大橙子",
avatar_url: "https://p3-passport.byteimg.com/img/user-avatar/346a615dd202d44344742d6b177c0f65~100x100.awebp",
},
]
const adminUsersQuery = useQuery(
["adminUsers"],
fetchAdminUsers,
{
initialData: hardCodedAdminUsers,
}
)
拓展一下上面的例子,假设你为数据设置了过期时间(staleTime
),那么此时传入的initialData
将会在这个时间(staleTime
)前都是最新(fresh
)的,即使你触发了默认刷新的动作,但是由于数据不是过期状态(slate
),此时react-query也不会帮你刷新数据。
不过你可以设置一个initialDataUpdatedAt
选项来解决这个问题,initialData
将会在initialDataUpdatedAt
+ staleTime
时间后过期。
例子如下:
const adminUsersQuery = useQuery(
["adminUsers"],
fetchAdminUsers,
{
initialData: hardCodedAdminUsers,
staleTime: 1000 * 60 * 60 * 24, // 1 day
initialDataUpdatedAt: 1677073883689, // 2023-02-22 21:51:23(中国标准时间)
}
)
上面的例子中,这个hardCodedAdminUsers
数据在2023-02-23 21:51:23
之前都不会过期,因此不会触发任何自动化的刷新查询操作,不过一旦过期后,如果满足条件就会触发刷新查询操作。
从其它查询中获取初始化数据(Initial Data)
在平时的开发中,大家都有经验,往往展示的列表页中的信息,通常都会包含许多详情页的数据,此时就可以使用列表页面的数据,作为初始化数据:
假如你有一个列表页查询是这样的:
const issuesListQuery = useQuery(
["issues", repo, owner],
fetchIssues
);
此时列表页数据中会包含很多列表项数据,此时一旦用户打开详情页,就在之前加载过的列表页数据中,寻找当前详情页的数据信息,作为initialData
,假代码如下:
const queryClient = useQueryClient();
const issueDetailQuery = useQuery(
["issue", repo, owner, issueNumber],
fetchIssue,
{
initialData: () => {
// ①
const issues = queryClient.getQueryData(["issues", repo, owner])
if (!issues) return undefined;
// ②
const issue = issues.find(issue => issue.number === issueNumber)
return issue;
}
},
)
①:通过queryClient
查询当前缓存中所有的列表数据内容
②:从缓存中寻找当前详情页需要的缓存内容
如果你不需要缓存数据,也可以作为
placeholderData
数据传入。
getQueryState
在之前硬编码管理员的例子中,可以自己手动传入初始化数据的生成时间。但是在刚刚的例子中,这个数据是从别的查询中获得的,该怎么设置时间呢?
react-query提供了queryClient.getQueryState
方法,让你可以知道该查询被查询了多少次、查询是否包含错误以及最后一次的更新时间(dataUpdatedAt
)。
因此可以向initialDataUpdatedAt
中传入这个值,例子如下:
const issueDetailQuery = useQuery(
["issue", repo, owner, issueNumber],
fetchIssue,
{
staleTime: 1000 * 60,
initialData: () => {
const issues = queryClient.getQueryData(["issues", repo, owner])
if (!issues) return undefined;
const issue = issues.find(issue => issue.number === issueNumber)
return issue;
},
initialDataUpdatedAt: () => {
const {dataUpdatedAt} = queryClient.getQueryState(["issues", repo, owner])
return dataUpdatedAt;
}
},
)
此时你就可以精确地控制缓存的过期时间,将新旧数据做很好的区分,方便react-query更合理地帮你处理相关查询。
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 19 天,点击查看活动详情