离线React Query

date
Jan 8, 2023
slug
offline-react-query
status
Published
tags
Tech
summary
type
Post
本文是对原文的翻译,如有疏漏还望指正。
我已经说了一遍又一遍——React Query 是一个异步的状态管理器。就像你给它一个Promise,不管是resolved 还是rejected,它都会很开心。你不用在乎Promise是从哪里来的。
产生Promise有太多方式,但就目前而言最大的用处便是获取数据。这非常频繁,但请求需要一个有效的网络连接。但有时,特别是在移动设备上的网络连接并非十分可靠,你需要你的应用在上述的情况下也能正常运行。

v3中的问题

React Query 非常适合处理离线的场景。因为它提供了缓存层,当缓存被填入,就能在无网络连接的情况下运行。让我们看一些在v3无法如期运行的边缘应用场景。我会用基本的发布列表 / 在文档中发布详细例子用于阐述:

1)缓存中无数据

正如我说的,在v3中,只要缓存被填入,应用就能很好地运行。一个奇怪的边缘应用场景如下:
  • 你有良好的网络并跳转到列表视图
  • 你在无连接的情形下点击文章
notion image
发生了什么使你的query查询会一直处于 loading 直到你重新获取到网络连接。当然,你也可以在浏览器的开发者工具中看到一个网络请求失败。那时因为React Query 会一直触发第一个请求,如果发送失败了,在离线情况下它将会暂停重试。
再者,React Query Devtools 会显示你的query 是 fetching ,这并不完全对。那query 实际上是 paused ,但我们没有一个概念去表示这个状态 —— 这是一个隐藏的实现细节。

2)没有重试

同样,如果你在上述场景中同时关闭了重试,你的query会马上转为error 状态且无法阻止。
notion image
为什么在我无网络的情况下,我们的query 需要 重试 才能 暂停 🤷‍♂️ ?

3)queries并不需要网络

queries并不需要网络来运行(如:因为它们在web worker 中进行了代价昂贵的异步处理)如果它们因为一些其它的原因失败了,它们会暂停直到你重新获取到网络连接。同时,那些queries在窗口聚焦时无法运行,因为当你无网络连接时那功能是完全无法使用的。

总的来说,有两个主要问题:在一些情况下,React Query 在可能不需要网络连接的情况下假定为需要(情况3),和在一些别的场景下,React Query 会触发请求即便是它可能不需要的情况下(情况1和2)。

新的NetworkMode

在v4中,我们已经尝试使用新的 networkMode 配置来完全地解决这些问题。有了新的 networkMode ,我们就能清晰地区分 onlineoffline 的queries。它是 useQueryuseMutation 中的一个选项,这就意味着你能针对全局或每一查询进行设置。毕竟,你可能有些需要网络连接的queries,有些则不需要。

online

这是v4中新的默认配置,正如我们所料大多数用户使用React Query结合数据获取。简而言之,有了这个配置,我们就假定一个query只能在有可用网络的情形下运行。
那么如果我没有可用网络但又运行一个query会发生什么呢?这个query会进入一个新的 paused 状态。这个 paused 状态仅次于 idleloadingsuccesserror 等主状态,因为你可以在任何时候脱离网络连接。
这意味着你的query可以同时处于 successpaused 状态。例如,你已经成功获取了一次数据但是后台的refetch 是 paused 状态。
或者,你可以处于 loadingpaused 状态如果一个query是首次挂载的情况。

fetchStatus

我们一直使用 isFetching 标志来指明一个query是正在运行的。类似于新的 paused 状态,一个query可以同时处于 successfetching 状态,或者它可以是 errorfetching 状态。后台重新获取(refetch)为你创造 许多 可能的状态(👋 状态机)。
正如 fetchingpaused 是互斥的那样,我们把它们整合进 useQuery 新的返回 fetchStatus 中去:
  • fetching :query在执行中 —— 请求正在进行。
  • paused:query未在执行中 —— 它被暂停了,直到重获网络连接。
  • idle:query当前并未在运行中。
就经验而言,query的 status 会给你相关 data 的信息:success 意味着你将一直拥有数据,loading 意味着你尚未拿到数据。我有想过把 loading 状态重命名为 pending ,但最后认为,这可能“too breaking”了。 😅
另一方面,fetchStatus 为你提供了 queryFn 的信息:它是不是正在运行?isFetchingisPaused 标志正是从 fetchStatus 中派生出去的。

让我们看看上面的情况1 在v4中会怎么样。请注意RQ devtools中新的切换网络模式的按钮。这很酷对吧因为它并没有真正地切断你的网络 —— 这只是让 React Query 相信 这是一个无网络环境以达到测试的目的。当然,我也以此为豪。😊
notion image
由于新的紫色标志,我们能够清楚地看到query的状态为(paused)。一旦我们重新开启网络环境,首次请求便会完成。

always

在此模式下,React Query不再关心你的网络连接与否。Queries 将会始终触发,永不暂停。当你使用React Query进行一些其它的获取数据操作时将会非常管用。

offlineFirst

此模式与React Query在v3中情况非常类似。始终 发出首次请求,如果失败,再次发送便会暂停。此模式适用于你有像浏览器缓存等类似的附加缓存层在 React Query之上。
让我们以GitHub repo API 为例。它发送如下响应头:
这意味着在接下来的60秒内,如果你再次请求资源,响应的数据将会来自于浏览器缓存。巧妙的是它也可以离线使用。Service workers,例如离线优先的PWAs,以类似的方式运行,通过拦截网络请求并在有可用缓存的情况下返回缓存的响应。
在React Query决定 触发请求时这些都不会发生,因为你处于无网络状态,就像默认的 online 那样。拦截一个获取数据请求的话才会发生 :) ,所以如果你有额外的缓存层,请确保使用 networkMode : offlineFirst .
如果第一个请求打出的同时命中了缓存 —— 真棒,你的query 将会是 success 状态,你将会获取到数据。如果命中缓存失败,你将可能被返回一个网络错误的error, 之后React Query将暂停再次获取并将你的query 置为 paused 状态。二者均对世界有益。🙌

这一切对我来说究竟意味着什么

没什么,除非你想这么做。你依旧可以决定忽略新的 fetchStatus 并只检查 isLoading 状态 —— React Query 将一如既往地做出反应(那么,在情况2 会表现更好,因为你将不会看见任何网络错误)。
然而,如果你想让你的应用在无网络连接的情况下保持健壮并以此为首要目标时,你就可以选择在对暴露出来的 fetchStatus 做出反应并采取行动。
对新的状态采取什么措施取决于你。我迫不及待地想看到那些在用户体验领域的人们在此之上会构建出什么。🚀

© JimLuo 2021 - 2024