很早之前就看到 React Query 在前端掘金圈子里火了一把,一直想学却因为种种原因没有开始。这一阵子闲下来了一些,快速学习了一下并且在自己的小项目里上手用了一番,体验非常不错。所以在这里以 React Query 为例写一篇 TanStack Query 小小的快速上手指南。

React Query

TanStack Query (FKA React Query) is often described as the missing data-fetching library for web applications, but in more technical terms, it makes fetching, caching, synchronizing and updating server state in your web applications a breeze.

TanStack Query(前称 React Query,在下文中我们也采用这个称呼)通常被形容为 Web 应用所缺失的数据请求库。更专业的说法是,他让你的 web 应用中的请求、缓存、同步与更新服务端状态更加轻而易举了。

从我这段时间的使用体验来看,其中对于请求与缓存的处理可以说是 React Query 给我带来的最显著的提升。

以这样一段请求为例👇🏻

 1function getListData() {
 2  return axios.get('https://xxxx');
 3}
 4
 5function List() {
 6  
 7  const [data, setData] = useState([])
 8  
 9  const [loading, setLoading] = useState(false)
10  
11
12  const queryData = () => {
13    setLoading(true)
14    getListData().then((res) => {
15      
16      setData(res.data.data)
17    }).catch((error) => {
18      
19    }).finally(() => {
20      setLoading(false)
21    })
22  }
23
24  useEffect(() => {
25    
26    if (fulfilled) {
27      queryData();
28    }
29  }, [])
30
31  
32  
33}

使用 React Query 管理请求后,我们可以这样写(用过 ahooks 的 useRequest 的小伙伴应该很熟悉这种写法)

 1function List() {
 2  
 3  const { data, isFetching } = useQuery({
 4    queryFn: getListData, 
 5    queryKey: ["list", "query"], 
 6    enabled: fulfilled, 
 7  });
 8
 9  
10  
11}

在外层,我们需要创建一个 QueryClient 并使用 QueryClientProvider 来提供一个查询的上下文。

 1const queryClient = new QueryClient()
 2
 3function App() {
 4  return (
 5    <QueryClientProvider client={queryClient}>
 6      <Todos />
 7    </QueryClientProvider>
 8  )
 9}

我们把冗长的请求逻辑全部都交由 React Query 来帮我们处理,我们只需要关心请求的状态和数据即可。

更详细一些

让我们聚焦在例子中 useQuery 这个方法上

 1const { data, isFetching } = useQuery({
 2  queryFn: getListData, 
 3  queryKey: ["list", "query"], 
 4  enabled: fulfilled, 
 5});

入参

对于入参,这里我们就需要介绍一下 React Query 中最关键的两个参数:queryFnqueryKey

查询函数(queryFn)需要是能够返回 Promise 的任意函数。而该 Promise 的返回值将会被填充至 useQuery 返回的 结果数据 data 或是 错误 error

查询键(queryKey)需要是一个数组,其元素可以是任何可以被序列化的类型。

当然, React作为一个开箱即用的库,其本身还包含着一些默认配置。如果你发现 Devtool 的网络 tab 中时不时冒出来一个预期意外的请求。不要着急提Bug,这可能是以下几个默认开启的配置项在“搞鬼”。

  • refetchOnMount:在组件挂载时自动请求
  • refetchOnWindowFocus:在页面被重新focus时自动请求
  • refetchOnReconnect:网络重新连接时自动请求

除此之外,还有一种配置会导致重复的请求行为。不过这个需要手动开启,一般不会在开发者不知情的情况下触发请求。

  • refetchInterval:定时请求

对于那些默认的配置,你可以通过在 useQuery 的入参中关闭他们,或者在创建 queryClient 的时候就将他们全局关闭。

 1const queryClient = new QueryClient({
 2  defaultOptions: {
 3    queries: {
 4      refetchOnReconnect: false,
 5      refetchOnWindowFocus: false,
 6      
 7    },
 8  },
 9});

返回

对于 useQuery 返回的结果,一翻文档发现有25个属性

 1const {
 2	data,
 3	dataUpdatedAt,
 4	error,
 5	errorUpdateCount,
 6	errorUpdatedAt,
 7	failureCount,
 8	failureReason,
 9	fetchStatus,
10	isError,
11	isFetched,
12	isFetchedAfterMount,
13	isFetching,
14	isInitialLoading,
15	isLoading,
16	isLoadingError,
17	isPaused,
18	isPlaceholderData,
19	isPreviousData,
20	isRefetchError,
21	isRefetching,
22	isStale,
23	isSuccess,
24	refetch,
25	remove,
26	status
27} = useQuery(options);

大多数变量都可以见名知意,但是有一些细心的小伙伴可能发现了,这里的状态有非常多种,看起来貌似非常复杂。别急,让我们细细梳理一番。

在 React Query 中,我们有两个独立的状态 fetchStatusstatus ,分别对应请求本身的状态和结果的状态。什么意思嘞?

请求本身的状态 fetchStatus ,也就是指我们的请求函数 queryFn 的状态,它在执行还是在摸鱼?useQuery 给了我们以下几种状态。当 fetchStatus 为 xx 时,

  1. fetching:该请求函数正在卖力干活!对应 isFetching
  2. pause:请求函数本应干活,但是因为网络等原因被迫暂停了。对应 isPause
  3. idle:该请求函数正在卖力摸鱼(空闲)!对应 isIdle

status 则是对结果 data 的状态描述,我的数据是一个什么样的状态?当 status 为 xx 时,

  1. loading:你先别急,还没数据。对应 isLoading
  2. error:你的数据遇到了点错误,具体信息可以看看返回值中的error。对应 isError
  3. success:你的数据安全获取到了,存在 data 里啦。对应 isSuccess

这些状态一般来说就足以支撑我们绝大多数的场景了。

关于查询键

其实了解了上面的内容,已经可以开始上手开发了。不过对于查询键,咱还是想要多说两句。React Query 作为一个请求管理库,核心就是要将不同的请求统一管理。而对于不同的请求,React Query 通过唯一的 queryKey 来进行标识分组

举个🌰,对于这些个 query

 1const queryA = useQuery(["a1", "b1", "c1"], queryFnA);
 2const queryB = useQuery(["a1", "b1", "c2"], queryFnB);
 3const queryC = useQuery(["a1", "b2", "c1"], queryFnC);
 4const queryD = useQuery(["a2", "b1", "c1"], queryFnD);

你可以想象成 React Query 维护了这样的一个集合

在未来,我们需要用 React Query 的更高级的功能时,特别是对一些特定的 query 进行操作时,我们只需要通过 queryKey 就可以轻松查找到我们想要的 query 了。

例如我需要找出 queryA 和 queryB ,那么我就可以通过 ["a1", "b1"] 来筛选得到想要的结果。

所以,在设计或者编写 queryKey 的时候,建议“三思而后行”。

最后

到这里,这篇小小的指南也要走到尽头了,但 React Query 才刚刚开始,在本文中,我们只讨论了最基础的查询,如果你想实现更多场景,例如分页,无限滚动等。亦或是向服务端发送删除,更新一类的请求(useMutation)。还是需要在文档中继续学习一番。

这里还推荐一下修仙大橙子dalao的专栏,也可以配合文档食用~

在阅读 React Query 的文档时,我发现了这样一段话

Out of the box, TanStack Query is configured with aggressive but sane defaults. Sometimes these defaults can catch new users off guard or make learning/debugging difficult if they are unknown by the user.

TanStack 是开箱即用的,使用了一套侵入性但健全的出厂配置。如果不知道这些出厂配置的话,可能会使新用户猝不及防,或是让他们的学习和debug变得困难。

想到我很早之前想学习 React Query 的时候,也是受到了某些“坑”的影响,转身干别的事去了(不可取不可取)。我希望在读完这篇小小的指南后,可以帮助你们平缓地度过这一段陡坡~

个人笔记记录 2021 ~ 2025