react-query v4 学习笔记(三) (缓存)

340 阅读3分钟

缓存

缓存涉及到的东西可能有点多,所以这部分先写一点。官网一个例子可以参考下

1. 缓存做了什么

简单的来说,就是把每次请求都保存在了内存,下次请求前会先取缓存,再对比响应更新数据。

做个简单的示例:

图片.png

  • 子组件和父组件都会请求同一个api地址,初始情况下父组件请求标题1-1 ~ 标题1-5,子组件请求标题2-1 ~ 2-5
  • 标题1和标题2通过传的参数来区分,也就是图片上的参数a和参数b
// 父组件querys = {} 子组件querys={a: "1", b: "1"}
const posts = useQuery({
    queryKey: ['posts', querys],
    queryFn: () => getPosts(querys),
})

1.1 初始渲染时

父组件没有参数,所以请求回来了标题1-x

子组件默认携带参数{a: 1, b: 1},所以请求回来了标题2-x

1.2 父组件传递和子组件相同的参数

在输入框里输入1,点击提交,父组件此时传递参数{a: 1, b: 1}

图片.png

这里注意:

  • 请求并没有结束fetchStatus仍然是fetching,但是数据已经变成了新的,这是因为直接取了缓存
  • 因为参数不一样,所以仍然会重新请求

2. staleTime过期时间

这是很常见的一个配置选项,在query中默认是0代表请求完了就过期。 当你设置了refetchOnMount, refetchOnWindowFocus, refetchOnReconnectrefetchInterval时,如果数据已经过期,就会重新请求。

  • 生成query实例初始化时,一般是从inactive状态恢复到active时,如果你配置了refetchOnMount: false,那么也不会自动请求
  • 重新聚焦网页时
  • 网络发生变化时

3. cacheTime请求对象回收清理时间

默认是5分钟,很容易和staleTime搞混,在网上也有很多人讨论,我看了个大概说说我的理解。

想象一下这种情况:

  1. 项目开启了refetchOnWindowFocus,这样过期(stale)的数据都可以在重新聚焦的时候重新请求,更新。
  2. 现在有两个模块,模块A请求api-1、api-1-1、api-1-2、api-1-3....等,模块2请求api-2...等请求。
  3. 用户长时间停留在模块2操作,所以模块的1的请求肯定是过期了,如果没有设置cacheTime,那么当用户重新聚焦页面时,除了模块2的数据重新请求了,模块1的一堆请求也会被发出去,显然没有必要

所以cacheTime的作用简单来说就是,当组件被卸载、切换了路由或者其他情况导致不能监听这个请求对象的情况下,过多长时间回收掉(想象成垃圾回收机制)。

举个例子:

  1. 左边开发面版可以看到有几个请求实例,当前页面在react-query 图片.png
  2. 现在我切换到测试页面,发现所有的请求实例都变成了inactive图片.png
  3. 我设置了cacheTime5 * 1000也就是五秒,所以五秒之后会发现不活跃的请求都被清理掉了。 图片.png

4. Query Invalidation手动过期数据

我发现使用react-query需要摒弃以前的思想,以前使用其他库的时候动不动就refetch,而在这里面,只有过期的数据和不一样的数据才需要重新获取,所以提供了invalidateQueries

具体api就不赘述了,主要讲讲和refetch的区别。这里有个讨论

  1. invalidateQueries只是标记,而不是直接重写请求
  2. invalidateQueries有更细腻灵活的配置,可以精确到某些请求
// 比如这个invalidateQueries,精确到参数version>10的请求
queryClient.invalidateQueries({
  predicate: (query) =>
    query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10,
})

// 这个请求会被设置为过期
const todoListQuery = useQuery({
  queryKey: ['todos', { version: 20 }],
  queryFn: fetchTodoList,
})

// 这个请求会被设置为过期
const todoListQuery = useQuery({
  queryKey: ['todos', { version: 10 }],
  queryFn: fetchTodoList,
})

// 这个请求不会
const todoListQuery = useQuery({
  queryKey: ['todos', { version: 5 }],
  queryFn: fetchTodoList,
})

5.待续。。。。