React Query入门指南:简化React应用中的信息获取

news/2025/10/18 20:59:19/文章来源:https://www.cnblogs.com/tlnshuju/p/19150148

React Query入门指南:简化React应用中的信息获取

2025-10-18 20:53  tlnshuju  阅读(0)  评论(0)    收藏  举报

文章目录

    • 引言
    • 为什么需要React Query?
    • 安装配置
    • 基础概念
      • 使用useQuery获取数据
      • queryKey的重要性
    • 实用技巧与进阶用法
      • 数据转换
      • 依赖查询
      • 使用useMutation修改数据
      • 乐观更新
      • 无限滚动/加载更多
    • 性能优化技巧
      • 缓存时间与失效时间
      • 手动控制重新获取
    • 实际项目中的最佳实践
      • 自定义Hooks
      • 预取数据
      • 全局错误处理
    • 何时不使用React Query
    • 结语

引言

前端开发中,数据获取一直是个老大难问题!!!尤其在React应用中,管理服务器状态、处理加载状态、错误处理以及缓存等都需要大量样板代码。很多开发者(包括我)都曾经被这些重复性工作折磨过…

React Query就是为解决这些痛点而生的强大开源库。它自称是"React缺失的数据获取库",而且这个说法一点都不夸张(认真脸)。今天我们就来一探究竟,看看这个库到底能帮我们解决什么问题,以及如何上手使用它。

为什么需要React Query?

在深入学习之前,我们先思考一下:传统的React数据获取方式有什么问题?

// 传统数据获取方式
function ProductList() {const [data, setData] = useState(null);const [isLoading, setIsLoading] = useState(true);const [error, setError] = useState(null);useEffect(() => {fetch('/api/products').then(res => res.json()).then(data => {setData(data);setIsLoading(false);}).catch(err => {setError(err);setIsLoading(false);});}, []);if (isLoading) return 

加载中...

;if (error) return

出错了: {error.message}

;return (
    {data.map(product => (
  • {product.name}
  • ))}
); }

看着挺简单,但随着应用复杂度增加,我们会遇到以下问题:

  1. 大量重复代码 - 每个组件都需要处理加载、错误、数据存储逻辑
  2. 缓存管理困难 - 何时刷新数据?如何避免重复请求?
  3. 数据同步 - 多个组件使用相同数据时如何保持同步?
  4. 分页/无限滚动 - 实现起来特别繁琐
  5. 乐观更新 - 提升用户体验的重要技术,但实现复杂

React Query几乎解决了以上所有问题(不是吹!)。它提供了一套声明式API,让数据获取变得简单而强大。

安装配置

首先,我们需要安装React Query:

# npm
npm install @tanstack/react-query
# yarn
yarn add @tanstack/react-query
# pnpm
pnpm add @tanstack/react-query

然后,在应用根部设置QueryClient和Provider:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
// 创建一个client
const queryClient = new QueryClient()
function App() {return (// 提供client给应用{/* 应用的其他部分 */}{/* 开发工具(可选但超有用!) */})
}

准备工作完成,现在可以开始使用React Query了!

基础概念

React Query的核心概念非常简单:

  • Queries: 获取数据(GET请求)
  • Mutations: 修改数据(POST, PUT, DELETE等请求)
  • Query Invalidation: 使缓存失效,触发重新获取

先来看看最基础的使用方式:

使用useQuery获取数据

import { useQuery } from '@tanstack/react-query'
function TodoList() {const { isLoading, error, data } = useQuery({queryKey: ['todos'],queryFn: () => fetch('/api/todos').then(res => res.json())})if (isLoading) return 
加载中...
if (error) return
出错了: {error.message}
return (
    {data.map(todo => (
  • {todo.title}
  • ))}
) }

就这么简单!但别被这表面的简单所迷惑,useQuery做了很多幕后工作:

  • 自动处理加载和错误状态
  • 缓存查询结果
  • 自动重试失败的请求
  • 支持轮询和定期刷新
  • 窗口聚焦时自动重新获取数据
  • 支持分页和无限滚动

queryKey的重要性

queryKey是React Query的核心概念(超级重要)。它不仅用于标识查询,还决定了何时重新获取数据。

// 基本查询
useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
// 带参数的查询
useQuery({queryKey: ['todos', { status, page }],queryFn: () => fetchTodos(status, page)
})

当依赖项(如status或page)变化时,React Query会自动重新获取数据。太智能了!

实用技巧与进阶用法

数据转换

有时API返回的数据格式可能不是组件需要的,我们可以使用select选项进行转换:

const { data: formattedTodos } = useQuery({queryKey: ['todos'],queryFn: fetchTodos,select: (data) => data.map(todo => ({...todo,formattedDate: new Date(todo.createdAt).toLocaleDateString()}))
})

依赖查询

某些查询可能依赖于其他查询的结果:

// 先获取用户
const { data: user } = useQuery({queryKey: ['user', userId],queryFn: () => fetchUser(userId)
})
// 再根据用户获取其待办事项
const { data: todos } = useQuery({queryKey: ['todos', user?.id],queryFn: () => fetchTodosByUser(user.id),// 只有当用户数据存在时才执行enabled: !!user
})

这种方式非常适合处理有依赖关系的API调用。

使用useMutation修改数据

对于POST、PUT、DELETE等请求,我们使用useMutation:

import { useMutation, useQueryClient } from '@tanstack/react-query'
function AddTodo() {const queryClient = useQueryClient()const [title, setTitle] = useState('')const mutation = useMutation({mutationFn: (newTodo) => {return fetch('/api/todos', {method: 'POST',body: JSON.stringify(newTodo)})},onSuccess: () => {// 成功后使相关查询失效,触发重新获取queryClient.invalidateQueries({ queryKey: ['todos'] })setTitle('')}})return (
{e.preventDefault()mutation.mutate({ title })}}> setTitle(e.target.value)}/>
) }

乐观更新

乐观更新是提升用户体验的重要技术,通过假设请求会成功,立即更新UI:

const queryClient = useQueryClient()
const mutation = useMutation({mutationFn: updateTodo,// 请求发出前先更新缓存onMutate: async (newTodo) => {// 取消可能冲突的请求await queryClient.cancelQueries({ queryKey: ['todos'] })// 保存旧数据用于回滚const previousTodos = queryClient.getQueryData(['todos'])// 乐观更新queryClient.setQueryData(['todos'], old =>old.map(todo => todo.id === newTodo.id ? newTodo : todo))return { previousTodos }},// 发生错误时回滚onError: (err, newTodo, context) => {queryClient.setQueryData(['todos'], context.previousTodos)},// 成功或失败后都要重新获取onSettled: () => {queryClient.invalidateQueries({ queryKey: ['todos'] })}
})

无限滚动/加载更多

React Query让实现无限滚动变得超级简单:

import { useInfiniteQuery } from '@tanstack/react-query'
function InfiniteTodos() {const {data,fetchNextPage,hasNextPage,isFetchingNextPage} = useInfiniteQuery({queryKey: ['todos', 'infinite'],queryFn: ({ pageParam = 1 }) => fetchTodoPage(pageParam),getNextPageParam: (lastPage) => lastPage.nextPage || undefined})return (
{data?.pages.map((page, i) => ({page.todos.map(todo => (
{todo.title}
))}
))}
) }

性能优化技巧

React Query已经内置了很多性能优化,但了解这些选项可以让你更好地调整:

缓存时间与失效时间

const queryClient = new QueryClient({defaultOptions: {queries: {// 数据缓存时间(默认5分钟)gcTime: 1000 * 60 * 5,// 数据被认为是新鲜的时间(默认0)staleTime: 1000 * 60,// 重试次数retry: 3,// 窗口重新聚焦时重新获取(默认true)refetchOnWindowFocus: true,},},
})

手动控制重新获取

有时你可能想手动控制数据刷新:

function TodoList() {const { data, refetch } = useQuery({queryKey: ['todos'],queryFn: fetchTodos,// 禁用自动重新获取refetchOnWindowFocus: false,refetchOnMount: false,staleTime: Infinity})return (
{/* 渲染数据 */}
) }

实际项目中的最佳实践

在我的经验中,以下做法能让React Query在大型项目中更好用:

自定义Hooks

将查询逻辑封装到自定义hooks中:

// hooks/useTodos.js
export function useTodos(filters) {return useQuery({queryKey: ['todos', filters],queryFn: () => fetchTodos(filters)})
}
export function useAddTodo() {const queryClient = useQueryClient()return useMutation({mutationFn: addTodo,onSuccess: () => {queryClient.invalidateQueries({ queryKey: ['todos'] })}})
}
// 使用
function TodoApp() {const { data, isLoading } = useTodos({ status: 'active' })const addTodoMutation = useAddTodo()// ...使用数据和mutation
}

预取数据

对于重要页面,可以预先获取数据提升用户体验:

// 在路由变化或用户hover某个链接时预取
function prefetchTodoPage(todoId) {queryClient.prefetchQuery({queryKey: ['todo', todoId],queryFn: () => fetchTodoById(todoId)})
}

全局错误处理

设置全局错误处理逻辑:

const queryClient = new QueryClient({queryCache: new QueryCache({onError: (error, query) => {// 处理所有查询错误if (error.status === 401) {// 重定向到登录页}console.error(`查询失败: ${query.queryKey}`);},}),mutationCache: new MutationCache({onError: (error) => {// 处理所有mutation错误toast.error(`操作失败: ${error.message}`)}}),
})

何时不使用React Query

虽然React Query很强大,但并非所有场景都适合:

  1. 客户端状态管理 - 对于纯本地状态,Redux或Context可能更合适
  2. 极简应用 - 如果应用非常简单,可能不值得引入额外库
  3. 不需要缓存的API调用 - 某些一次性操作可能不需要React Query

结语

React Query彻底改变了我处理服务器数据的方式。它优雅地解决了React应用中数据获取的绝大多数问题,让我可以专注于业务逻辑而不是重复编写数据获取的样板代码。

我强烈建议任何中大型React应用都考虑使用React Query。它会让你的代码更加简洁、可维护,并提供出色的用户体验。别忘了查阅官方文档,那里有更多高级功能等待探索!

希望这篇入门指南对你有所帮助。编码愉快!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/939761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

征程 6 | 征程 6 工具链如何支持 Matmul/Conv 双 int16 输入量化?

一、前言 在征程 6 工具链的量化精度调优实践中,当 Matmul/Conv 算子的两个输入皆为量化敏感算子时,PTQ 与 QAT 各自面临着难以克服的棘手困境。 在 PTQ 流程里,为了实现双 int16 输入的支持,工作人员不得不借助脚…

做题技巧与结论证明

优先考虑单调性。 优先考虑 dfn 区间。 优先考虑直径。 优先考虑写 gf。 这个可能是假的 优先考虑凸性。 优先考虑必要条件,再补一补断言这是充要条件。 优先考虑最大元。 结论: 谁跟你结论,斜杠糗大了

CSP-S模拟34/2025多校冲刺CSP模拟赛6

写在前面:废话 坏坏坏坏坏坏坏,假假假假假假假,唐唐唐唐唐唐唐,爆爆爆爆爆爆爆,蒻蒻蒻蒻蒻蒻蒻!!!!111P推歌 《幸福安心委员会》 どうして みんなが幸せなの? 为什么 大家都这么幸福呢? この世界のこと 闻…

Java学习通互评5

1.StudenDaoListImpl.java与StudentDaoArrayImpl.java有何不同? 前者用ArrayList,后者用普通数组;前者无需指定初始容量,后者需在创建对象时传入数组大小;前者支持动态扩容,后者容量固定,数组满后无法继续添加;…

卡车厂实习第三天

1、每个活可以一个一个干,也可以串联起来干,串联起来更省时省力,找技巧,还得知道为什么冷媒充700或者900或者1700,玻璃水为了降本加一瓶的量。 2、今天充冷媒的时候发现充不进去,可能是接头活塞在拧螺丝的时候碎…

随机数技术

随机数应用、生成方法、随机性检验随机数应用密钥 加密:不确定算法引入随机数实现 签名 认证:抗重放、假冒 密钥协商安全要求:随机性分布均匀性:0,1频率大致相等 独立性:任何子序列不能有其他子序列推导不可预测性…

我做 AI 算法 10 年,见过最可惜的事:技术再牛,说不明白也是白搭

我做 AI 算法 10 年,见过最可惜的事:技术再牛,说不明白也是白搭大家好,我是李老师。过去 5 年在互联网大厂做 AI 落地时,我见过不少 “技术大牛”—— 有人能把自然语言处理模型调得比行业均值精准 10%,有人能在…

第六周作业---定时器

1.定义LED引脚2.定义全局变量3.声明需要调用的函数4.设计简单的延时函数5.配置GPIO 编写一个GPIO的配置函数,其中包含使能GPIO时钟,配置LED引脚以及配置USART1引脚。6.配置USART1 编写一个USART1的配置函数,其中包含…

『普及』浅谈图的基础

基础知识 图是一种网状数据结构,用于描述对象的集合以及对象之间的关系。其中,对象用顶点表示,也称为节点,简称点。对象之间的关系用连接顶点的边表示。若图中的每条边是单向的,则该图称为有向图;若图中的每条边…

运筹学在供应链优化中的实际应用

本文介绍了两位博士后研究员在某中心供应链优化技术团队的研究工作,重点探讨了运筹学在库存优化、路径规划和需求预测等实际业务问题中的应用,包括算法建模和系统优化方法。运筹学在现实问题中的应用:某中心博士后研…

P6715 [CCO 2018] Fun Palace 题解

Description 有一个含 \(n\) 个点的链,每个点有一个权值。 对于任何 \(1\le i\le n-1\),都有一条边连接 \((i,i+1)\)。 您可以在链中的任意一些节点放置一些生物。 对于第 \(i\) 条边,若点 \(i\) 至少有 \(a_i\) 个…

被C语言链表折磨的一天 Σ( △ |||)︴

OIer竟沦落到这个地步——>C语言课要免修就要拿C语言来写一个链表,而且不能拿数组模拟 ( ̄△ ̄;),为了免修,OIer只好学习一手小小指针。 首先需要知道指针是什么: int *a char *a[] double *a 以上三个都是指针…

WebGL学习及项目实战(第03期:绘制多个点,线,面)

@目录目标WebGL原理示意图着色器代码顶点着色器代码解析片元着色器代码解析js代码初始化WebGL代码初始化shaders代码创建着色器对象创建程序对象初始化buffers代码绘制代码代码总结绘制多个点完整代码初始化缓冲区绘制…

ozon定制尺寸和重量

ozon定制尺寸和重量VVV---A888---KKK

CF 359D. Pair of Numbers

D.Pair of Numbers原题链接 题意简述西蒙有一个数组 \(a_1, a_2, ..., a_n\) ,由 n 个正整数组成。今天,西蒙要求你找出一对整数 $l, r (1 \leq l \leq r \leq n) $,使得下列条件成立: 有整数 \(j ( l …

2025多校CSP模拟赛6

T1:最长不下降子序列 (sequence) 思路: 依据做传统最长不下降子序列的的经验,这题用 \(dp\) 。 因为 \(a\) 的值只有 \(1,2\) ,并且翻转操作只进行一次,所以我们考虑什么样的情况一次翻转能产生最长不下降子序列呢…

Java基础——类型转换,变量、常亮、作用域,基本运算符

Java基础——类型转换,变量、常亮、作用域,基本运算符类型转换 从低到高--------------> byte,short,char-->int-->long-->float-->double (小数优先级高于整数) 运算过程中需要将不同类型的值转换…

洛谷 LGR-246 S 模拟赛

100 + 0 + 44 + 24 = 168, Rank 469/3708.下次再也不在过掉了一个题的情况下试图去想第二个题的正解了 /ll【LGR-246】SCP 2025 第二轮(复赛 S 组)模拟 链接:link 题解:题目题解区内 时间:4h (2025.10.18 14:30~1…

godot3D节点本身的偏转数值错误竟会导致空间移动穿模??!

在Godot引擎中,当RigidBody3D节点被施加一个异常扭矩值apply_torque((-inf,inf,-inf))时,即使不会导致物体实际旋转,也会引发异常物理行为:物体在空间线性力或自由落体作用下会匀速穿模。测试表明,RigidBody3D的姿…

Kafka面试精讲 Day 24:Spring Kafka构建实战

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …