React Query 查询的三阶段
date
Nov 4, 2025
slug
react-query-stage
status
Published
tags
React Query
summary
React Query 查询的三阶段
type
Post
好了好了,同学们安静!上节课的“共享自习室”还记得吧?咱们学会了怎么用“去重”来避免重复劳动,让组件们学会了“抄作业”的精髓。
今天,咱们要玩点更真实的。
React Query 最神奇的地方之一,就是它能让你写异步代码,感觉就像在写同步代码一样——行云流水,德芙纵享丝滑。但问题是,它只是感觉像,它并不是真的!
这就像你点了一份外卖。在 App 上下单很快,感觉食物已经到手了。但你不能立刻开始吃,对吧?中间有个“等待外卖小哥”的过程。
“幻觉”与现实的碰撞
让我们来搞个新需求:我们要获取用户电脑上所有可用的摄像头、麦克风设备列表。浏览器正好有个
navigator.mediaDevices.enumerateDevices() API,它返回一个 Promise,完美!凭着我们对 React Query 的了解,我们可能会“想当然”地写出这样的代码:
看起来很美,不是吗?
queryFn 返回一个 Promise,React Query 搞定一切,我们直接用 data 就好了。但如果你真的运行这段代码,迎接你的将是一个鲜红的错误:
Cannot read properties of undefined (reading 'map')这是新手最常踩的坑,没有之一!
为什么?因为 React Query 不是时间机器!它无法瞬间变出数据。在你调用
useQuery 的那一刻,到 queryFn 里的 Promise 完成之前,有一段“真空期”。在这段时间里,data 是什么?是 undefined!你试图在一个
undefined 上调用 .map() 方法,这就像你对着空气说:“嘿,空气,给我变个披萨出来!” JavaScript 引擎只会一脸懵逼地看着你,然后给你报个错。救星登场:查询的“人生三阶段”
那么,我们怎么知道外卖送到哪一步了呢?很简单,看外卖 App 上的状态啊!“正在备餐”、“骑手已取货”、“正在派送”...
React Query 也为我们提供了这样一个“状态追踪器”。每一个查询(Query),都有它清晰的“人生三阶段”,这完全对应了 Promise 的三种状态:
pending(等待中):查询已经发出,正在路上,我们还没有拿到数据。就像外卖 App 上的“正在备餐”。此时data是undefined。
success(已成功):查询成功返回,数据已到手!就像外卖 App 上的“订单已送达”。此时data是可用的。
error(出错了):查询失败了,可能网络断了,或者服务器宕机了。就像外卖 App 提示“商家关门,订单取消”。此时data还是undefined,但你会得到一个error对象。
我们有两种方式来获取这个状态。
方式一:直截了当的 status 属性
useQuery 会直接返回一个 status 字符串,告诉你当前处于哪个阶段。看,通过判断
status,我们为用户提供了完美的加载和错误提示,那个恼人的 undefined 错误也随之消失了。方式二:更具“风味”的布尔值标志
如果你觉得写字符串比较麻烦,React Query 还提供了一套派生出来的布尔值,用起来更像是开关。
isPending(是不是在pending?)
isSuccess(是不是success了?)
isError(是不是error了?)
代码就变成了这样:
用
status 还是用 isPending/isError?这纯粹是个人口味问题。就像你喜欢用 tabs 还是 spaces 一样。你可以随便选一个,然后在和同事的代码审查(Code Review)中,为你的选择据理力争、捍卫尊严!Promise 状态 | 查询人生阶段 | status 属性 | is 系列布尔值 |
Pending | 等待中 | 'pending' | isPending: true |
Fulfilled | 已成功 | 'success' | isSuccess: true |
Rejected | 出错了 | 'error' | isError: true |
给 TypeScript 同学的悄悄话
这里有个更酷的魔法。
useQuery 的返回对象是一个“可区分联合类型”。说人话就是:TypeScript 非常聪明!
当你写下
if (isPending) 或者 if (status === 'error') 这样的判断后,在这些代码块的内部,TypeScript 知道 data 肯定是 undefined。而一旦你排除了所有这些可能性,在剩下的代码里(也就是成功状态下),TypeScript 会自动把
data 的类型从 MediaDeviceInfo[] | undefined 收窄为 MediaDeviceInfo[]。它知道,外卖送到家了,那手里拿的就一定是披萨,不可能是“披萨或者空气”!这个过程叫**“类型收窄”**,让你在写代码时享受极致的类型安全和自动补全。课堂总结
好了同学们,今天的核心知识点就是:
- 警惕异步幻觉:React Query 虽好,但它不是时间机器。数据需要时间才能到达。
- 拥抱生命周期:所有查询都有
pending、success、error三个阶段。
- 做好状态处理:使用
status或isPending/isError来为用户提供加载和错误界面,这是专业前端的必备素养!
记住,一个优秀的 UI,不仅要展示成功的结果,更要优雅地处理等待和失败。现在,你已经掌握了构建健壮异步界面的关键钥匙。
下课!去把你们代码里那些裸奔的
data.map 都抓起来,给它们穿上 isPending 的加载外套和 isError 的错误提示吧!