ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

React Query 之 Queries 完整指南:useQuery、唯一键与数据状态机

React Query 之 Queries 完整指南:useQuery、唯一键与数据状态机 React Query 之 Queries 完整指南useQuery、唯一键与数据状态机【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query本文基于当前仓库TanStack Query monorepo含 React Query 等六大框架适配的官方指南 Queries 文档系统讲解在 React 应用中如何用useQuery声明式订阅一个异步数据源、queryKey/queryFn 分别承担什么职责以及status与fetchStatus两套状态各自表达什么信息。读完你将掌握查询结果的判别方法、如何在加载/错误/成功三种视图间安全渲染数据并理解这些状态在 query-core 底层是如何被推导出来的从而写出健壮且可复用的数据获取逻辑。Query 基础把异步数据当作声明式依赖在 TanStack Query 中query查询是对某个异步数据源的一种声明式依赖declarative dependency并且它必须与一个唯一键unique key绑定。这一概念的核心意涵是你不需要自己写何时取数据、取到后存哪里、何时刷新的样板逻辑只需要声明我依赖这份数据框架会负责后续的缓存、去重、重取与共享。一个 query 可以配合任何基于 Promise 的方法使用——既可以是 GET 也可以是 POST 请求——用于从服务器拉取数据。如果你的方法会修改服务器上的数据则不应使用 query而是应该使用 Mutations 指南中介绍的useMutation因为写操作天然需要不同的语义幂等、乐观更新、失效缓存等。在组件或自定义 Hook 中订阅一个 query只需要调用useQuery并至少提供两个要素该查询的唯一键queryKey一个返回 Promise 的函数queryFn该 Promise 要么resolve 出数据要么throw 一个错误最简单的形态如下import { useQuery } from tanstack/react-query function App() { const info useQuery({ queryKey: [todos], queryFn: fetchTodoList }) }需要注意自 v5 起useQuery只接受单个对象这一种调用签名不能再传入(queryKey, queryFn)分离参数。这一约束在 useBaseQuery.ts 中有明确校验开发环境下传入错误参数会直接抛错提示这是迁入 v5 的一个关键行为差异。唯一键缓存与共享的地基你提供的唯一键会被框架内部用于三件核心事务重新拉取refetching、缓存caching以及在整个应用内共享sharing这条查询。也就是说同一个组件树里多个地方只要使用相同的queryKey它们读到的就是同一份数据、同一种请求状态从而避免重复请求。关于键的设计哲学序列化规则、必须唯一、结构必须稳定可以进一步阅读 Query Keys 指南而queryFn的具体写法与约束如何接收QueryFunctionContext拿到 key 与信号量见 Query Functions。查询结果对象读懂三大核心状态useQuery返回的result对象携带了你在模板渲染与业务处理中所需要的全部关于该查询的信息const result useQuery({ queryKey: [todos], queryFn: fetchTodoList })其中最重要的是一组状态需要开发者熟练掌握。在任意时刻一个 query 只能处于以下三种status之一状态布尔快捷方式含义status pendingisPending查询还没有任何数据status errorisError查询遇到了错误status successisSuccess查询成功数据可用在主要状态之外根据查询所处阶段还能拿到更多信息error—— 当查询处于isError状态时可通过error属性取得具体的错误对象data—— 当查询处于isSuccess状态时可通过data属性取得数据isFetching—— 无论处于哪种状态只要查询此刻正在拉取包括后台重新拉取 background refetchisFetching就为true。这些派生布尔量在 types.ts 的类型定义中有完备声明isPending、isError、isFetching等都属于由status/fetchStatus派生出来、仅为方便而提供的布尔值。推荐的判别顺序pending → error → 直接渲染对绝大多数查询而言通常只需要依次检查isPending、isError之后即可假定数据已就绪并直接渲染成功态function Todos() { const { isPending, isError, data, error } useQuery({ queryKey: [todos], queryFn: fetchTodoList, }) if (isPending) { return spanLoading.../span } if (isError) { return spanError: {error.message}/span } // 到这里我们可以假定 isSuccess true return ( ul {data.map((todo) ( li key{todo.id}{todo.title}/li ))} /ul ) }另一种写法直接使用 status 字符串如果你更喜欢对字符串做判别而非布尔量也可以直接读取statusfunction Todos() { const { status, data, error } useQuery({ queryKey: [todos], queryFn: fetchTodoList, }) if (status pending) { return spanLoading.../span } if (status error) { return spanError: {error.message}/span } // 此时也是 status success但用 else 逻辑同样成立 return ( ul {data.map((todo) ( li key{todo.id}{todo.title}/li ))} /ul ) }两种写法各取所好效果完全等价。而且如果你先检查了pending与errorTypeScript 会把data的类型正确收窄为非undefined的类型——这是 v5 类型系统的显著改进types.ts 中针对不同状态重载了DefinedUseQueryResult与UseQueryResult等结果类型开发者可以在完全类型安全的前提下访问data。FetchStatus查询正在干什么在status之外useQuery还会返回一个附加的fetchStatus属性取值如下取值含义fetchStatus fetching查询此刻正在拉取fetchStatus paused查询想拉取但被暂停了详见 Network Mode 指南fetchStatus idle查询此刻什么都没做这三个取值的类型定义见 types.ts其中FetchStatus fetching | paused | idle。paused状态通常发生在网络不可用时当networkMode为默认的online或offlineFirst的离线分支且设备离线框架会把本想发起的请求挂起而不是立刻失败。这一点可以从 fetchState 的实现中直接看到——它根据canFetch(options.networkMode)的判定结果把新请求初始化为fetching或paused而canFetch见 retryer.ts的默认逻辑正是(networkMode ?? online) online。完整语义与配置方法见 Network Mode。为什么需要两套状态这是理解 Queries 的核心心智模型。因为存在后台重新拉取与stale-while-revalidate后台用新数据替换过期数据机制status与fetchStatus的所有组合都可能出现。例如一个处于success状态的查询fetchStatus通常是idle但当后台重新拉取发生时它同时会是fetching——数据仍是旧的、可立即展示的但正在被静默刷新一个刚挂载且没有任何数据的查询status通常是pending、fetchStatus是fetching但如果当时没有网络连接它可能是paused。因此必须记住查询可能处于pending状态但并没有真正在拉取数据。官方给出的经验法则非常精炼status描述的是关于data的信息我们到底有没有数据fetchStatus描述的是关于queryFn的信息它此刻是否在运行这解释了为什么isPending不等于isLoading在 queryObserver.ts 的结果装配中isLoading isPending isFetching、isRefetching isFetching !isPending、isPaused fetchStatus paused一整套派生布尔量正是围绕数据有无与请求是否在飞这两个正交维度展开的。源码视角这些状态是如何被计算出来的从实现层面看React 侧的useQuery本质上是QueryObserver观察者模式中的订阅者的薄封装它经由 useBaseQuery 在挂载时创建QueryObserver通过useSyncExternalStore订阅数据变更并调用observer.getOptimisticResult(...)在渲染前乐观地把结果置为 fetching 状态从而保证首帧渲染就能看到pending而不是闪烁一次空态。真正计算状态的地方在 createResult若存在placeholderData且status pending框架会把展示状态临时提升为success并标记isPlaceholderData相关配置见 Placeholder Query Data 指南随后按需执行select数据裁剪并处理select过程中可能抛出的错误最后统一装配status、fetchStatus以及全部派生布尔量并做浅比较去重仅在有变化时才通知订阅的组件重渲染这是 render-optimizations 主题的基石。而一次真实拉取开始时query.ts 会通过fetchState把查询状态切换到fetching离线则为paused成功则写入successState携带dataUpdatedAt时间戳失败则累积fetchFailureCount与failureReason供重试策略使用。从状态到 UI 的实战要点结合以上机制在实际页面中可以组合出丰富而准确的 UI 反馈例如用isFetching在已有数据的情况下渲染正在后台更新的轻提示function Todos() { const { status, data, error, isFetching } useQuery({ queryKey: [todos], queryFn: fetchTodoList, }) if (status pending) return Loading... if (status error) return spanError: {error.message}/span return ( div ul {data.map((todo) ( li key{todo.id}{todo.title}/li ))} /ul div{isFetching ? Background Updating... : }/div /div ) }此类后台拉取指示器的更多设计模式参见 Background Fetching Indicators 指南。本仓库还提供了可运行的最小示例 examples/react/basic其中完整的 Todo 列表即采用上述判别模式background-fetching-indicators 相关目录下的示例则演示了如何利用isFetching与status的组合呈现静默刷新。延伸阅读Query KeysqueryKey 的构造、序列化与结构共享规则Query FunctionsqueryFn 的签名、上下文参数与错误抛出约定Mutations当方法会修改服务器数据时改用useMutationNetwork Mode理解paused状态与离线行为Important DefaultsstaleTime、gcTime、重试等影响状态的默认配置Caching数据在缓存中的生命周期与状态迁移TypeScriptdata类型收窄与各状态重载的完整说明社区中关于状态检查的另一种写法亦有维护者 TkDodo 撰写的专门文章可供对照Status Checks in React Query你可以结合本文的判别顺序挑选最适合自己团队代码风格的统一模式。【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表