Blog
首页
文档
收藏
关于
  • 在线转换时间戳 (opens new window)
  • 在线压缩图片 (opens new window)
  • Float-Double转二进制 (opens new window)
  • 文件转Hex字符串 (opens new window)

HiuZing

🍑
首页
文档
收藏
关于
  • 在线转换时间戳 (opens new window)
  • 在线压缩图片 (opens new window)
  • Float-Double转二进制 (opens new window)
  • 文件转Hex字符串 (opens new window)
  • 前端面试题

  • JavaScript

  • Vue2

  • port

  • CSS

  • Node.js

  • JavaScript优化

  • uniapp

  • Mini Program

  • TypeScript

  • 面向对象编程

  • UI组件

  • Plugin

  • Vue3

  • 性能优化

  • Axios

  • 状态管理

  • React

    • 教程

    • 实战

    • 生态

      • TanStack Query
        • 基础使用
          • 配置
          • 基础前置概念
          • 常用操作
          • 查询状态
          • 时间状态
      • zustand
  • Mock

  • Icon

  • Template

  • 构建工具

  • 项目规范配置

  • Taro

  • SVG

  • React Native

  • 前端
  • React
  • 生态
HiuZing
2026-07-23
目录

TanStack Query

# 基础使用

# 配置

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

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60 * 1000,        // 数据 60 秒内视为新鲜
      gcTime: 5 * 60 * 1000,       // 未使用的缓存 5 分钟后回收
      retry: 3,                     // 失败重试 3 次
      refetchOnWindowFocus: true,   // 窗口聚焦时重新获取
    },
  },
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <MainApp />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 基础前置概念

useQuery:普通单次查询(详情、单页列表)

useInfiniteQuery:无限分页滚动查询

useMutation:新增、编辑、删除、表单提交

# useQuery

数据请求

  1. 基础语法

    const {
      data,        // 请求成功返回的数据
      isLoading,   // 初次加载 loading
      isFetching,  // 任意请求(初次/后台刷新)
      isError,     // 请求失败
      error,       // 错误对象
      refetch,     // 手动重新请求
      status,      // 'loading' | 'success' | 'error'
    } = useQuery({
      queryKey: ['user', id], // 唯一缓存标识,参数变化自动重请求
      queryFn: () => getUserApi(id), // 请求函数,必须返回 Promise
      // 会继承你全局配置 staleTime/gcTime/retry
      staleTime: 60 * 1000,
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  2. 关键参数

    queryKey
    queryFn
    
    1
    2
  3. 最简示例

    import { useQuery } from '@tanstack/react-query';
    
    const getUserInfo = async (userId) => {
      const res = await fetch(`/api/user/${userId}`);
      return res.json();
    };
    
    function UserInfo({ userId }) {
      const { data, isLoading, isError } = useQuery({
        queryKey: ['user', userId],
        queryFn: () => getUserInfo(userId),
      });
    
      if (isLoading) return <div>加载用户信息...</div>;
      if (isError) return <div>加载失败</div>;
    
      return <div>用户名:{data.name}</div>;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  4. 用手动操作

    // 强制刷新接口,忽略缓存
    refetch()
    // 清除缓存,下次渲染重新请求
    queryClient.invalidateQueries(['user', userId])
    
    1
    2
    3
    4

# useInfiniteQuery

分页增强版

  1. 基础语法

    const {
      data,                // { pages: 每页数据[], pageParams: 分页参数[] }
      fetchNextPage,       // 加载下一页
      hasNextPage,         // 是否存在下一页
      isFetchingNextPage,  // 加载下一页loading
      isLoading,           // 第一页初次加载
    } = useInfiniteQuery({
      queryKey: ['article', type],
      // pageParam 分页标识,默认初始值由 getNextPageParam 控制
      queryFn: ({ pageParam = 1 }) => getListApi(pageParam),
      // 核心配置:提取下一页参数,返回 null/undefined 代表无更多
      getNextPageParam: (lastPage) => lastPage.nextPage,
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

# useMutation

数据修改

  1. 基础语法

    const {
      mutate,         // 触发提交的函数(同步,不阻塞渲染)
      mutateAsync,    // 返回Promise,支持await
      isPending,      // 请求进行中loading
      isSuccess,      // 请求成功
      isError,        // 请求失败
      error,
    } = useMutation({
      mutationFn: (params) => submitApi(params), // 提交接口
      // 生命周期回调
      onSuccess: (res, variables) => {
        // 请求成功后刷新列表缓存,更新页面数据
        queryClient.invalidateQueries(['article']);
      },
      onError: (err) => {
        console.error('提交失败', err);
      },
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  2. 参数说明

    // mutationFn
    接收调用 mutate(参数) 传入的变量,执行提交请求
    
    // mutate 回调形式
    mutate(formData, {
      onSuccess: () => alert('提交成功')
    })
    
    // mutateAsync 支持 async await
    const handleSubmit = async () => {
      await mutateAsync(formData);
      router.push('/list');
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  3. 完整示例

    import { useMutation, useQueryClient } from '@tanstack/react-query';
    
    const addArticleApi = async (data) => {
      const res = await fetch('/api/article', {
        method: 'POST',
        body: JSON.stringify(data)
      });
      return res.json();
    };
    
    function AddArticle() {
      const queryClient = useQueryClient();
      const { mutate, isPending } = useMutation({
        mutationFn: addArticleApi,
        onSuccess: () => {
          // 失效列表缓存,页面自动刷新最新数据
          queryClient.invalidateQueries(['article']);
        },
      });
    
      const submit = () => {
        mutate({ title: '新文章', content: '内容' });
      };
    
      return <button onClick={submit} disabled={isPending}>{isPending ? '提交中' : '新增'}</button>;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
  4. 乐观更新

    提交前直接修改缓存,无需等待接口返回,提升体验,失败自动回滚

    // 纯乐观更新,不重新请求
    useMutation({
      mutationFn: addArticleApi,
      onMutate: async (newItem) => {
        await queryClient.cancelQueries(['article']);
        // 保存旧数据用于回滚
        const oldData = queryClient.getQueryData(['article']);
        // 手动修改缓存,提前展示新增数据
        queryClient.setQueryData(['article'], (old) => ({
          ...old,
          pages: [{ list: [newItem, ...old.pages[0].list] }, ...old.pages.slice(1)]
        }));
        return { oldData };
      },
      // 请求失败,恢复原始缓存
      onError: (err, vars, context) => {
        queryClient.setQueryData(['article'], context.oldData);
      },
    });
    
    // 乐观更新 + 成功后刷新真实数据
    useMutation({
      mutationFn: addArticleApi,
      onMutate: async (newItem) => {
        await queryClient.cancelQueries(['article']);
        const oldData = queryClient.getQueryData(['article']);
        queryClient.setQueryData(['article'], (old) => ({
          ...old,
          pages: [{ list: [newItem, ...old.pages[0].list] }, ...old.pages.slice(1)]
        }));
        return { oldData };
      },
      onError: (err, vars, context) => {
        queryClient.setQueryData(['article'], context.oldData);
      },
      // 请求完成(无论成功失败),重新拉取真实列表覆盖本地临时数据
      onSettled: () => {
        queryClient.invalidateQueries(['article']);
      }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
  5. 注意事项

    Mutation 成功后必须 invalidateQueries 刷新对应 Query,保证页面数据同步;

# 常用操作

queryClient 是全局缓存控制器,用来手动操控缓存、触发请求,分为四大类:失效刷新、直接修改缓存、预加载、其他辅助

# invalidateQueries

标记缓存过期,自动重发请求

// 清空全部
queryClient.invalidateQueries();

// 前缀匹配失效
// 匹配所有以 todos 开头的缓存:['todos']、['todos', 1]、['todos', page] 都会失效。
queryClient.invalidateQueries({ queryKey: ['todos'] });

// exact: true 精确匹配
// 只匹配完全一模一样的 key ['todos',1],不会波及 ['todos',2]、列表分页等其他缓存。
queryClient.invalidateQueries({ queryKey: ['todos', 1], exact: true });

// predicate 自定义规则筛选失效
// 满足条件才失效
queryClient.invalidateQueries({
  predicate: (query) => query.queryKey[0] === 'todos' && query.state.data?.length > 10,
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# setQueryData

本地直接修改缓存,不发网络请求

queryClient.setQueryData(['user', userId], (old) => ({
  ...old,
  name: newName,
}));
1
2
3
4

# prefetchQuery

预加载数据(提前缓存)

queryClient.prefetchQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
  staleTime: 60 * 1000,
});
1
2
3
4
5

# 查询状态

                ┌──────────┐
                │  fresh   │ ← staleTime 内
                └────┬─────┘
                     │ staleTime 过期
                ┌────▼─────┐
          ┌────→│  stale   │ ← 下次使用时后台刷新
          │     └────┬─────┘
          │          │ 组件卸载(无观察者)
          │     ┌────▼─────┐
          │     │ inactive │ ← gcTime 倒计时
          │     └────┬─────┘
          │          │ gcTime 过期
          │     ┌────▼─────┐
          │     │ deleted  │ ← 缓存清除
          │     └──────────┘
          │
          └── refetch / invalidate → 重新获取后回到 fresh

# 时间状态

staleTime(新鲜时间):
  数据在多长时间内被认为是"新鲜"的
  新鲜 → 不会后台重新获取
  过期 → 下次使用时触发后台重新获取

gcTime(垃圾回收时间,原 cacheTime):
  查询没有活跃观察者后,缓存在内存中保留多长时间
  超时后缓存被删除

时间线示例(staleTime=60s, gcTime=300s):
  0s:   组件 A 发起查询 → 请求 → 缓存数据
  30s:  组件 B 使用相同 key → 直接返回缓存(数据仍新鲜)
  60s:  数据过期(stale)
  90s:  组件 C 使用相同 key → 先返回缓存 → 后台重新获取 → 更新
  120s: 所有组件卸载(无观察者)
  420s: gcTime 到期 → 缓存删除
  421s: 新组件查询 → 无缓存 → isLoading=true → 重新请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
上次更新: 2026/07/23, 15:04:03
React Redux实战
zustand

← React Redux实战 zustand→

最近更新
01
zustand
07-24
02
软路由
04-06
03
基础知识
04-06
更多文章>
Theme by Vdoing | Copyright © 2021-2026 WeiXiaojing | 友情链接
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式