React Query 的一大优势是可以轻松访问查询的状态字段。你能够立即知道你的查询是否正在加载或是否有误。为此,库暴露了几个布尔标志,这些标志主要来自内部状态机。你的查询可以处于以下状态之一:
success:你的查询成功并且你有数据error:你的查询无效,并设置了错误loading:你的查询没有数据,目前正在第一次加载idle:你的查询从未运行,因为它未启用
请注意, isFetching 标志不是内部状态机的一部分,它是一个附加标志,只要请求正在进行中就会为真。你可以获取和成功,也可以获取和错误 - 但不能同时加载和成功。状态机确保了这一点。
标准示例
空闲状态大多被排除在外,因为它是禁用查询的边缘情况。所以大多数例子看起来像这样:
const todos = useTodos()
if (todos.isLoading) {
return 'Loading...'
}
if (todos.error) {
return 'An error has occurred: ' + todos.error.message
}
return <div>{todos.data.map(renderTodo)}</div>
在这里,我们首先检查加载和错误,然后显示我们的数据。这可能适用于某些用例,但不适用于其他用例。许多数据获取解决方案,尤其是手工制作的解决方案,没有重新获取机制,或者仅在显式用户交互时重新获取。
但是 React Query 可以。
默认情况下,它会非常积极地重新获取,并且无需用户主动请求重新获取。 refetchOnMount、refetchOnWindowFocus 和 refetchOnReconnect 的概念非常适合保持数据准确,但如果此类自动后台重新获取失败,它们可能会导致用户体验混乱。
后台错误
在许多情况下,如果后台重新获取失败,它可以被默默地忽略。但是上面的代码并没有这样做。让我们看两个例子:
- 用户打开一个页面,初始查询加载成功。他们在页面上工作了一段时间,然后切换浏览器选项卡以查看电子邮件。几分钟后他们回来了,
React Query将进行后台重新获取。但是现在获取失败。 - 我们的用户在具有列表视图的页面上,他们单击一项以深入查看详细信息视图。运行正常,之后他们返回到列表视图。一旦他们再次进入详细视图,他们将看到缓存中的数据。这很棒,除非后台重新获取失败。 在这两种情况下,我们的查询都将处于以下状态:
{
"status": "error",
"error": { "message": "Something went wrong" },
"data": [{ ... }]
}
如你所见,我们将同时获得错误和陈旧数据。这就是 React Query 伟大的原因——它包含 stale-while-revalidate 缓存机制,这意味着它总是会在数据存在时为你提供数据,即使它是陈旧的。
现在由我们来决定我们显示什么。显示错误很重要吗?如果我们有陈旧数据,仅显示陈旧数据就足够了吗?我们是否应该同时显示两者,也许带有一点背景错误指示器?
这个问题没有明确的答案 - 这取决于你的确切用例。但是,鉴于上面的两个示例,我认为如果将数据替换为错误屏幕,那将是一种有点令人困惑的用户体验。
当我们考虑到 React Query 默认情况下会使用指数退避重试失败的查询 3 次时,适当的处理很重要,可能需要几秒钟的时间才能将过时的数据替换为错误屏幕。如果你也没有后台获取指示器,这可能真的令人困惑。
这就是我通常首先检查数据可用性的原因:
const todos = useTodos()
if (todos.data) {
return <div>{todos.data.map(renderTodo)}</div>
}
if (todos.error) {
return 'An error has occurred: ' + todos.error.message
}
return 'Loading...'
同样,没有明确的正确原则,因为它高度依赖于用例。每个人都应该意识到主动重新获取的后果,我们必须相应地构建我们的代码,而不是严格遵循简单的 Todo样例程序 😉。