为什么很多大项目都在用 @tanstack/react-query?它解决了什么问题

更新日期: 2025-10-15 阅读: 108 标签: 项目

如果你在做 react 项目,并且需要从后端获取数据,那你很可能遇到过这些情况:

  • 同样的数据在不同组件里重复请求

  • 用户操作后,界面数据没有及时更新

  • 加载状态和错误状态需要自己处理

  • 分页、无限滚动等功能实现起来很麻烦

@tanstack/react-query(原名 React Query)就是专门为解决这些问题而生的。它是一个专门管理服务器状态的数据获取库。


它能帮你做什么

简单来说,react-query 帮你处理所有跟后台数据相关的工作:

  • 自动缓存数据,避免重复请求

  • 数据过期后自动重新获取

  • 错误自动重试

  • 乐观更新(先更新界面,再发送请求)

  • 分页和无限滚动支持

你不再需要把这些逻辑写在 Redux 或者 Context 里,代码会简洁很多。


开始使用

安装:

pnpm add @tanstack/react-query

在应用最外层设置:

import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import Layout from '@/Layout';

export default function App() {
  const queryClient = new QueryClient();
  
  return (
    <QueryClientProvider client={queryClient}>
      <Layout />
    </QueryClientProvider>
  );
}

这样你的所有组件就都能使用 react-query 的功能了。


获取数据:useQuery

看一个获取用户信息的例子:

import React from 'react';
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

// 定义获取数据的函数
function fetchUser(userId) {
  return axios.get(`https://jsonplaceholder.typicode.com/users/${userId}`)
    .then(res => res.data);
}

function User({ userId }) {
  const { data, isLoading, isError, error, refetch } = useQuery({
    queryKey: ['user', userId],  // 缓存的键名
    queryFn: () => fetchUser(userId),  // 获取数据的函数
    staleTime: 1000 * 60,  // 1分钟内数据是新鲜的,不会重新请求
    cacheTime: 1000 * 60 * 5,  // 数据缓存5分钟
    retry: 2,  // 失败自动重试2次
  });

  if (isLoading) return <div>加载中...</div>;
  if (isError) return <div>出错了: {error.message}</div>;

  return (
    <div>
      <h2>{data.name}</h2>
      <p>邮箱: {data.email}</p>
      <button onClick={() => refetch()}>重新加载</button>
    </div>
  );
}

这里有几个重要的参数:

  • queryKey:数据的唯一标识,相同 key 的数据会被缓存和复用

  • staleTime:数据保鲜时间,这段时间内不会重新请求

  • cacheTime:数据缓存时间,即使组件卸载了数据还会保留


修改数据:useMutation

当你需要添加、修改或删除数据时,可以用 useMutation:

import React, { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';

// 获取待办列表
const fetchTodos = () => axios.get('/api/todos').then(res => res.data);

// 添加新待办
const addTodo = (text) => axios.post('/api/todos', { text }).then(res => res.data);

export function Todos() {
  const queryClient = useQueryClient();
  const [newText, setNewText] = useState('');

  // 获取待办列表
  const { data: todos = [] } = useQuery({ 
    queryKey: ['todos'], 
    queryFn: fetchTodos 
  });

  // 添加新待办
  const mutation = useMutation({
    mutationFn: addTodo,
    
    // 乐观更新:先更新界面,再发送请求
    onMutate: async (text) => {
      // 取消正在进行的请求,避免冲突
      await queryClient.cancelQueries({ queryKey: ['todos'] });
      
      // 保存当前数据,用于出错时回滚
      const previousTodos = queryClient.getQueryData(['todos']);
      
      // 立即更新缓存
      queryClient.setQueryData(['todos'], [...(previousTodos || []), { 
        id: Date.now(), 
        text,
        // 临时标识,等真实请求成功后会替换
        optimistic: true 
      }]);
      
      return { previousTodos };
    },
    
    // 出错时回滚到之前的状态
    onError: (err, variables, context) => {
      if (context?.previousTodos) {
        queryClient.setQueryData(['todos'], context.previousTodos);
      }
    },
    
    // 无论成功失败,都重新获取最新数据
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] });
    }
  });

  return (
    <div>
      <h2>待办列表</h2>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            {todo.text}
            {todo.optimistic && ' (保存中...)'}
          </li>
        ))}
      </ul>

      <input 
        value={newText} 
        onChange={e => setNewText(e.target.value)} 
        placeholder="输入新待办" 
      />
      <button onClick={() => mutation.mutate(newText)}>
        添加
      </button>
    </div>
  );
}

乐观更新让用户体验更好,用户不用等待请求完成就能看到结果。


处理数据依赖关系

有时候你需要先获取一个数据,然后基于这个数据获取另一个数据:

// 先获取用户信息
const userQuery = useQuery({
  queryKey: ['userByName', username],
  queryFn: () => axios.get(`/api/users?username=${username}`).then(res => res.data),
});

// 等获取到用户ID后,再获取用户的文章
const postsQuery = useQuery({
  queryKey: ['posts', userQuery.data?.id],
  queryFn: () => axios.get(`/api/users/${userQuery.data.id}/posts`).then(res => res.data),
  // 只有拿到用户ID后才执行
  enabled: !!userQuery.data?.id,
});

enabled 参数让你能够控制什么时候执行请求。


开发工具

react-query 提供了开发工具,让你在开发时能看到缓存的数据、正在进行的请求等信息:

import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Layout />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

安装开发工具包:

pnpm add @tanstack/react-query-devtools


实际项目中的建议

  1. 统一管理查询键名:把所有的 queryKey 放在一个文件里管理,避免拼写错误

  2. 封装自定义 Hooks:把 useQuery 和 useMutation 封装成自定义 Hook,方便复用

  3. 设置全局默认值:可以配置默认的 staleTime 和 cacheTime

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5分钟
      cacheTime: 1000 * 60 * 30, // 30分钟
    },
  },
});


总结

@tanstack/react-query 解决了 React 项目中数据管理的核心问题。它让你的代码更简洁,用户体验更好,开发效率更高。虽然需要一些学习成本,但一旦用起来,你会发现它大大简化了数据获取和状态管理的工作。

如果你的项目中有复杂的数据获取需求,或者你厌倦了自己处理加载状态和缓存逻辑,那么很值得尝试一下 react-query。

本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!

链接: https://fly63.com/article/detial/13022

程序员最喜欢什么样的项目经理?

在当今的专业环境中,项目经理需要戴上各种帽子,在管理团队的日常功能和理解大局策略之间切换。正因为如此,项目经理对组织变得更有价值,并且他们对技能和战略角色的需求在全球范围内不断增长。但这也提出了一个问题:如何在如此高压的环境中成为更好的项目经理?

原生JS实现随机点名项目

随机产生规定范围内的整数,然后再产生相同范围内的整数,两者相同时,则暂停。所用知识:Math.random() * num: 产生从0到num的随机数,Math.floor(): 向下取整,简单的DOM操作等

没有项目经验找工作处处碰壁怎么办?

我马上就要毕业了没有开发经验怎么办?我投递了 N 多公司全部没有给工作机会,有的给了面试机会也是没有下文了怎么办?我简历上什么东西都没有,要不要伪造一个工作经历呢?

当了项目经理才明白的10件事!

项目经理这个神奇的职位,改变了我很多工作处事的方式,从前性情纯真的耿直boy,现在变成了人鬼皆爱的老油条, 以下是我当了项目经理之后明白的10件事, 如有雷同,真是太巧。

pm2 快速部署前端项目

pm2 大家应该都知道,主要是用来管理 node 进程,但是同样可以用来部署前端代码。也可以手动添加 public key 到服务器上的 ~/.ssh/authorized_keys,

关于小型长周期项目的一些建议

我不是专业的项目经理,这里不讨论大型项目管理的事情。我们比较常遇到的可能是小型的长周期项目,比如2-4个人,做半年甚至一年的项目。这种项目通常不会有专职的项目经理

水印项目的实现以及两种实现方案的选优

通过 attachShadow 这个方法生成一个shadow root 即shadow的根节点,然后在这个根节点下面通过循环语句添加水印,利用position为absolute进行排版,使其铺满容器

重构项目,你真的准备好了吗?

我相信每个接受过老项目的程序员可能都吐槽过“前人的代码都是屎”。一个已经有些年头的项目,几乎肯定可以看到——到处拷贝来拷贝去的代码,随处可见的拼写错误,头重脚轻的函数……

浅谈RPC

近几年随着微服务化项目的崛起,逐渐成为许多公司中大型分布式系统架构的主流方式,而今天所说的 RPC 在这其中扮演着至关重要的角色。随着这段日子公司项目微服务化的演进,发现在日常开发中都在隐式或显式的使用 RPC

在Vue项目中使用Eslint+Prettier+Stylelint

首先搭建vue项目,lint选择ESLint + Prettier,配置方式选择In dedicated config files。具体搭建过程这里就不赘述了,如果不熟悉的同学可以点击这里。配置 Stylelint,目前还没有stylelint选项,需要我们自己安装相关的 npm 包

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!