React Query 中的状态检查

本文正在参加「金石计划 . 瓜分6万现金大奖」

本文翻译自 TkDodoStatus Checks in React Query

React Query 的一个优势是可以简单获取 query 的状态,你可以立即知道 query 是在 loading 或者 它出错了,为此,该库暴露出来了一连串的 boolean 标识,其中大部分都来自于内部的状态机,查看这些类型,你的查询状态可以是以下之一

  • success:你的 query 是成功的,并且你拥有数据
  • error:你的 query 没有成功,并且设置了一个错误
  • loading:你的 query 当前没数据,并且是第一次加载
  • idle:你的 query 还没有运行过,因为它未开启 (enable)

更新:idleReact Query v4 中已经被删除

注意 isFetching 不是内部状态机的一部分,它是一个可选的标识,当正在请求时,它会为 true,你可以同时处在 fetchingsuccess 的状态,你可以同时处在 fetchingerror 的状态,但是你不可能同时处在 successloading 的状态,内部的状态机确保了这一点

更新:在 React Query v4,isFetching 是从 fetchStatus 派生出来的,就比如 isPaused,你可以从 #13: Offline React Query阅读更多

标准例子

idle 这里被排除在外,因为它是一个禁用的边缘例子,因此绝大多数例子看起来像这样

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>

我们首先检查了 loadingerror,然后展示了数据,这满足了大部分场景,但不是绝对的,很多数据请求的解决方案,特别是手写的,没有 refetching 机制,或者只有在用户触发时

但是 React Query 有,

默认情况下它的 refetching 是相当积极的,并且是在用户不主动触发的情况下,refetchOnMountrefetchOnWindowFocusrefetchOnReconnect 这些概念对于保持数据准确性是相当好的。但是如果一个后台的 refetch 失败可能会造成混乱的用户体验

后台错误

在大多数情况下,后台获取数据失败,是可以忽略的,但是上面的代码没有这么做,我们可以看两个例子

  • 用户打开了页面,然后初始化的 query 成功,他们在当前页面工作了一段时间,然后切换到了浏览器其它 tab 检查了电子邮件,几分钟后他们回到了当前页面,React Query 会做后台的 refetch,现在请求失败了

  • 用户打开了列表页面,然后点击一个其中一条进入了详情页面,之后他们返回了列表页面,一旦他们又再次进入了详情页面,他们将看到来此缓存的数据,这很棒 – 除非后台 refetch 失败

在这两种情况下 我们的查询都将处于以下的状态

{
  "status": "error",
  "error": { "message": "Something went wrong" },
  "data": [{ ... }]
}

就像你看到的,我们有一个 error 和一个 陈旧的数据可用,这就是 React Query 的伟大之处,它使用了 stale-while-revalidate 缓存机制,这代表如果数据存在,它会始终给你,即使它是陈旧的

现在由我们来决定显示什么,显示错误重要吗?如果我们有陈旧的数据,只显示陈旧的数据够用吗?或者我们应该同时显示它们(添加一个错误的背景提示)

这个问题没有一个明确的答案,它去取决于你的使用场景,但是对于上面的两个例子,我觉得如果数据被替换成了错误,这将会是一个混乱的用户体验

当我们考虑到 React Query 会默认在失败时使用 指数退避 请求三次的时候这点就更重要了,因此可能会出现需要几秒钟才能将陈旧的数据变为错误页面,如果你也没有后台获取的提示,这可能会令人很困惑

这就是为什么我通常首先检查数据的可用性

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...'

同样,这里没有什么明确的原则说明这是对的,它高度依赖你的使用场景,每个人都应该意识到积极地重新获取所带来的的后果,并且构建相应的代码

本文正在参加「金石计划 . 瓜分6万现金大奖」

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容