使用 react-query swr 之前,必看
react-query和swr的定位都是用作服务端状态管理的,但是它的使用方式很容易让人觉得它是数据请求的hook,千万别建立这样的惯性思维,否则你会遇到无尽的麻烦
在掘金 很多推荐swr的文章,例子里都有useState的身影,这完全是错误示范,想想看,真能这样用,为什么他们的官网文档,几乎不写useState的代码作为式例?
还是那句话,解决方法没有银弹,每个东西都有自己的使用场景,别滥用
何时使用react-query swr ?
先上结论:
- 用作服务端状态管理时,都可以,更推荐swr,因为size更小, 比如在 nuxtjs, remix 里使用
- 用作客户端请求库时,只推荐ahooks的useRequest
为什么不推荐使用它们处理客户端状态 ?
场景:
-
queryKey 变了, 不想请求?
- 比如对象数组作为key, 当数组顺序改变,并不想请求
- swr 和 react-query, 做不到
- ahooks 不使用 queryKey 作为依赖, 所以想请求就请求,不想请求就不请求
-
queryKey 没变, 想请求?
- 比如实时数据, 过一段时间,点击请求按钮,因为 key 没变, 所以不会请求
- ahooks 不使用 queryKey 作为依赖, 所以想请求就请求,不想请求就不请求
- 使用refetch 强刷? 有翻页参数时会导致请求两次:
page =1 & form不变, refetch() // 请求一次, ok
page =2 & form 不变, refetch() // 查询使业务需要重置page为1, 导致请求两次:page1一次, page2一次
page =2 & form 改变, refetch() // page 和 form 没有在 useEffect时修改时, 两次: refetch一次, 新参数一次; page 和 form 至少有一个是在 useEffect 里修改时, 至少三次,refetch一次, useEffect 外一次, useEffect里一次
如果你不care多请求,也行,毕竟不会导致竞态问题
-
批量接口重新请求 ?
- 比如切换全局筛选器, 想把整个页面的接口都刷新一遍
- react-query 的 queryClient.invalideQueries() 仅支持 get请求, swr没测试过, 且所有参数都需要加入queryKey, 比如 headers里有一个 gameId, 当改变 gameId 时并放入localStorage里, 调用invalideQueries() 从localStorage里取数据,这时header里的gameId 可能是新的也可能是旧的
- ahooks 没测试过
- react-query 和 swr 的单接口fetch
- 最后推荐 zustand 来做
-
单接口重新请求 ?
- react-query refetch() 不支持传参,所以你得维护一个是否触发请求的状态,queryKey 等同于 useEffect, 但是react推荐不要把事件处理程序里得逻辑放在uesEffect
- swr refetch() 支持传参,但是参数必须同时也在queryKey里,不然无效, 实属神经病设计
- ahooks refetch() 支持传参, 没有 queryKey 的限制,你可以在事件处理程序里调用refetch()
看到这,你会发现 useRequest 和普通的fetch 没什么两样, 除了它有一个方便的refetch,和处理了竞态问题, 以及减少了维护loading,error 状态, 不过对我来说, 多写几行 loading,error 状态代码没什么大不了,所以我还是使用 fetch
- 解决竞态问题, 但remix作者说,做竞态处理的项目很少,大多数时候 loading 时禁用一下按钮足够了
- 乐观更新,这玩意,我没遇到这样的场景,不做评价
还是那句话 如无必要,勿增实体,如果以上你都不care,强烈推荐ohfetch,随用随调
名词解释:
按照我国教育的惯例, 应该在文章开头就写,但是我不care纸上谈兵的理论, 所以在这普及一下
什么是服务端状态?
这是服务端渲染相关领域的概念,指在服务端渲染组件时,发起网络请求,获取到数据,这份数据称为服务端状态,有个特点: 该请求的请求参数不由用户交互(useState)产生的,在纯spa页面里,这个概念也适用,当一个请求无参数,或者全来自于prop,我们可以把这个组件放到服务端去渲染,因此也称为服务端状态
什么是客户端请求?
在客户端渲染期间发起的请求。即spa应用里的所有请求,可以把它们大致分为两类
- 请求参数由useState提供或部分提供,这主要是由用户交互修改,这部分请求不适合用swr去接管,这是我的血泪经验,同时也是react-query作者的意思
- 没有状态就是第二类,你可以使用swr去处理
服务端状态 相对于 客户端状态 真正的区别 ?
- 无参请求 /userInfo
- 请求参数只来自prop /detail(props.id)
为什么推荐在 nuxtjs, remix 里,才使用 swr react-query ?
nuxtjs 和 remix 支持服务端组件, 如果你有很多的服务端组件,并且需要共享状态, 那么使用它俩就真的很优雅
在spa应用中,很不推荐, 唯一的用处是:获取 userInfo时, 免去存到context的代码, 很多事件,甚至使用localStorage就够用了, 推荐 mantine的 useLocastorage, 它做了双向同步, 当 storage 改变, 会重新render所有使用了该组件的state, 同时state 改变也会同步到storage;大多数时候已经足够了, 再复杂一点, 可以上更优雅的 zustand
如果你在服务端状态里使用, 我也可以提供一点注意事项:
- 务必设置
staleTime:Infinity
, 这样才能达到一个全局状态管理的目的,否则返回值用作hook的依赖时,会造成无限循环 - 读缓存时,使用
useQuery
,不要使用queryClient.getCache
, 因为在按需使用时, 很难保证使用useQuery获取首次数据一定在getCache
之前, 这会加重心智负担,这也是完全可避免的
swr的优点
- 减少 context 或者 props 的传递
- 性能更好, 用到时才请求
- 多组件共享,减少请求数
- 比如多个菜单页面都会有城市下拉选择筛选, 那么只需要请求一次即可
- 尤其适用于开发table编辑功能, 需要行内联动切换多份数据源给Select组件使用时, 你可以完全把数据请求封装在select组件里,只会请求一次, 否则, 1. 你要么在顶层组件遍历请求所有情况, 2.要么封装请求到select组件,每一行都会请求
- 多页面共享,减少请求数
- SPA 应用, 切换菜单只会卸载组件, 因此多个菜单里都使用了这份数据时,可以减少请求数
何时不要使用 react-query(swr)?
- 你的请求参数有来自于用户交互(useState)
- 比如筛选表单,搜索表单,翻页 table 等, 虽然文档介绍了支持翻页, 但最好别用, 缺点大于优点, 什么乐观更新,都是鸡肋, 远远大于你为了使用它而付出的东西, 这也是作者的意思 yes because that's client state
转载自:https://juejin.cn/post/7223687532068077628